mirror of
https://github.com/flarum/framework.git
synced 2025-03-23 22:03:32 +08:00
feat: customizable session driver (#3610)
This commit is contained in:
parent
84c31165e5
commit
f6761843b2
42
framework/core/src/Extend/Session.php
Normal file
42
framework/core/src/Extend/Session.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Extend;
|
||||||
|
|
||||||
|
use Flarum\Extension\Extension;
|
||||||
|
use Illuminate\Contracts\Container\Container;
|
||||||
|
|
||||||
|
class Session implements ExtenderInterface
|
||||||
|
{
|
||||||
|
private $drivers = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new session driver.
|
||||||
|
*
|
||||||
|
* A driver can currently be selected by setting `session.driver` in `config.php`.
|
||||||
|
*
|
||||||
|
* @param string $name: The name of the driver.
|
||||||
|
* @param string $driverClass: The ::class attribute of the driver.
|
||||||
|
* Driver must implement `\Flarum\User\SessionDriverInterface`.
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function driver(string $name, string $driverClass): self
|
||||||
|
{
|
||||||
|
$this->drivers[$name] = $driverClass;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function extend(Container $container, Extension $extension = null)
|
||||||
|
{
|
||||||
|
$container->extend('flarum.session.drivers', function ($drivers) {
|
||||||
|
return array_merge($drivers, $this->drivers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -14,10 +14,14 @@ use Flarum\Extension\ExtensionManager;
|
|||||||
use Flarum\Foundation\Application;
|
use Flarum\Foundation\Application;
|
||||||
use Flarum\Foundation\Config;
|
use Flarum\Foundation\Config;
|
||||||
use Flarum\Settings\SettingsRepositoryInterface;
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
|
use Flarum\User\SessionManager;
|
||||||
use Illuminate\Contracts\Queue\Queue;
|
use Illuminate\Contracts\Queue\Queue;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use InvalidArgumentException;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
use SessionHandlerInterface;
|
||||||
use Symfony\Component\Console\Helper\Table;
|
use Symfony\Component\Console\Helper\Table;
|
||||||
use Symfony\Component\Console\Helper\TableStyle;
|
use Symfony\Component\Console\Helper\TableStyle;
|
||||||
|
|
||||||
@ -48,18 +52,32 @@ class InfoCommand extends AbstractCommand
|
|||||||
*/
|
*/
|
||||||
private $queue;
|
private $queue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SessionManager
|
||||||
|
*/
|
||||||
|
private $session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SessionHandlerInterface
|
||||||
|
*/
|
||||||
|
private $sessionHandler;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ExtensionManager $extensions,
|
ExtensionManager $extensions,
|
||||||
Config $config,
|
Config $config,
|
||||||
SettingsRepositoryInterface $settings,
|
SettingsRepositoryInterface $settings,
|
||||||
ConnectionInterface $db,
|
ConnectionInterface $db,
|
||||||
Queue $queue
|
Queue $queue,
|
||||||
|
SessionManager $session,
|
||||||
|
SessionHandlerInterface $sessionHandler
|
||||||
) {
|
) {
|
||||||
$this->extensions = $extensions;
|
$this->extensions = $extensions;
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
$this->settings = $settings;
|
$this->settings = $settings;
|
||||||
$this->db = $db;
|
$this->db = $db;
|
||||||
$this->queue = $queue;
|
$this->queue = $queue;
|
||||||
|
$this->session = $session;
|
||||||
|
$this->sessionHandler = $sessionHandler;
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
@ -93,6 +111,7 @@ class InfoCommand extends AbstractCommand
|
|||||||
$this->output->writeln('<info>Base URL:</info> '.$this->config->url());
|
$this->output->writeln('<info>Base URL:</info> '.$this->config->url());
|
||||||
$this->output->writeln('<info>Installation path:</info> '.getcwd());
|
$this->output->writeln('<info>Installation path:</info> '.getcwd());
|
||||||
$this->output->writeln('<info>Queue driver:</info> '.$this->identifyQueueDriver());
|
$this->output->writeln('<info>Queue driver:</info> '.$this->identifyQueueDriver());
|
||||||
|
$this->output->writeln('<info>Session driver:</info> '.$this->identifySessionDriver());
|
||||||
$this->output->writeln('<info>Mail driver:</info> '.$this->settings->get('mail_driver', 'unknown'));
|
$this->output->writeln('<info>Mail driver:</info> '.$this->settings->get('mail_driver', 'unknown'));
|
||||||
$this->output->writeln('<info>Debug mode:</info> '.($this->config->inDebugMode() ? '<error>ON</error>' : 'off'));
|
$this->output->writeln('<info>Debug mode:</info> '.($this->config->inDebugMode() ? '<error>ON</error>' : 'off'));
|
||||||
|
|
||||||
@ -169,4 +188,53 @@ class InfoCommand extends AbstractCommand
|
|||||||
{
|
{
|
||||||
return $this->db->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
|
return $this->db->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports on the session driver in use based on three scenarios:
|
||||||
|
* 1. If the configured session driver is valid and in use, it will be returned.
|
||||||
|
* 2. If the configured session driver is invalid, fallback to the default one and mention it.
|
||||||
|
* 3. If the actual used driver (i.e `session.handler`) is different from the current one (configured or default), mention it.
|
||||||
|
*/
|
||||||
|
private function identifySessionDriver(): string
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Get the configured driver and fallback to the default one.
|
||||||
|
*/
|
||||||
|
$defaultDriver = $this->session->getDefaultDriver();
|
||||||
|
$configuredDriver = Arr::get($this->config, 'session.driver', $defaultDriver);
|
||||||
|
$driver = $configuredDriver;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Try to get the configured driver instance.
|
||||||
|
// Driver instances are created on demand.
|
||||||
|
$this->session->driver($configuredDriver);
|
||||||
|
} catch (InvalidArgumentException $e) {
|
||||||
|
// An exception is thrown if the configured driver is not a valid driver.
|
||||||
|
// So we fallback to the default driver.
|
||||||
|
$driver = $defaultDriver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get actual driver name from its class name.
|
||||||
|
* And compare that to the current configured driver.
|
||||||
|
*/
|
||||||
|
// Get class name
|
||||||
|
$handlerName = get_class($this->sessionHandler);
|
||||||
|
// Drop the namespace
|
||||||
|
$handlerName = Str::afterLast($handlerName, '\\');
|
||||||
|
// Lowercase the class name
|
||||||
|
$handlerName = strtolower($handlerName);
|
||||||
|
// Drop everything like sessionhandler FileSessionHandler, DatabaseSessionHandler ..etc
|
||||||
|
$handlerName = str_replace('sessionhandler', '', $handlerName);
|
||||||
|
|
||||||
|
if ($driver !== $handlerName) {
|
||||||
|
return "$handlerName <comment>(Code override. Configured to <options=bold,underscore>$configuredDriver</>)</comment>";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($driver !== $configuredDriver) {
|
||||||
|
return "$driver <comment>(Fallback default driver. Configured to invalid driver <options=bold,underscore>$configuredDriver</>)</comment>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $driver;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
27
framework/core/src/User/SessionDriverInterface.php
Normal file
27
framework/core/src/User/SessionDriverInterface.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\User;
|
||||||
|
|
||||||
|
use Flarum\Foundation\Config;
|
||||||
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
|
use SessionHandlerInterface;
|
||||||
|
|
||||||
|
interface SessionDriverInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Build a session handler to handle sessions.
|
||||||
|
* Settings and configuration can either be pulled from the Flarum settings repository
|
||||||
|
* or the config.php file.
|
||||||
|
*
|
||||||
|
* @param SettingsRepositoryInterface $settings: An instance of the Flarum settings repository.
|
||||||
|
* @param Config $config: An instance of the wrapper class around `config.php`.
|
||||||
|
*/
|
||||||
|
public function build(SettingsRepositoryInterface $settings, Config $config): SessionHandlerInterface;
|
||||||
|
}
|
46
framework/core/src/User/SessionManager.php
Normal file
46
framework/core/src/User/SessionManager.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\User;
|
||||||
|
|
||||||
|
use Flarum\Foundation\Config;
|
||||||
|
use Illuminate\Session\SessionManager as IlluminateSessionManager;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use SessionHandlerInterface;
|
||||||
|
|
||||||
|
class SessionManager extends IlluminateSessionManager
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns the configured session handler.
|
||||||
|
* Picks up the driver from `config.php` using the `session.driver` item.
|
||||||
|
* Falls back to the default driver if the configured one is not available,
|
||||||
|
* and logs a critical error in that case.
|
||||||
|
*/
|
||||||
|
public function handler(): SessionHandlerInterface
|
||||||
|
{
|
||||||
|
$config = $this->container->make(Config::class);
|
||||||
|
$driverName = Arr::get($config, 'session.driver');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$driverInstance = parent::driver($driverName);
|
||||||
|
} catch (InvalidArgumentException $e) {
|
||||||
|
$defaultDriverName = $this->getDefaultDriver();
|
||||||
|
$driverInstance = parent::driver($defaultDriverName);
|
||||||
|
|
||||||
|
// But we will log a critical error to the webmaster.
|
||||||
|
$this->container->make(LoggerInterface::class)->critical(
|
||||||
|
"The configured session driver [$driverName] is not available. Falling back to default [$defaultDriverName]. Please check your configuration."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $driverInstance->getHandler();
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,9 @@
|
|||||||
namespace Flarum\User;
|
namespace Flarum\User;
|
||||||
|
|
||||||
use Flarum\Foundation\AbstractServiceProvider;
|
use Flarum\Foundation\AbstractServiceProvider;
|
||||||
use Illuminate\Session\FileSessionHandler;
|
use Flarum\Foundation\Config;
|
||||||
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
|
use Illuminate\Contracts\Container\Container;
|
||||||
use SessionHandlerInterface;
|
use SessionHandlerInterface;
|
||||||
|
|
||||||
class SessionServiceProvider extends AbstractServiceProvider
|
class SessionServiceProvider extends AbstractServiceProvider
|
||||||
@ -20,12 +22,42 @@ class SessionServiceProvider extends AbstractServiceProvider
|
|||||||
*/
|
*/
|
||||||
public function register()
|
public function register()
|
||||||
{
|
{
|
||||||
$this->container->singleton('session.handler', function ($container) {
|
$this->container->singleton('flarum.session.drivers', function () {
|
||||||
return new FileSessionHandler(
|
return [];
|
||||||
$container['files'],
|
});
|
||||||
$container['config']['session.files'],
|
|
||||||
$container['config']['session.lifetime']
|
$this->container->singleton('session', function (Container $container) {
|
||||||
);
|
$manager = new SessionManager($container);
|
||||||
|
$drivers = $container->make('flarum.session.drivers');
|
||||||
|
$settings = $container->make(SettingsRepositoryInterface::class);
|
||||||
|
$config = $container->make(Config::class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default to the file driver already defined by Laravel.
|
||||||
|
*
|
||||||
|
* @see \Illuminate\Session\SessionManager::createFileDriver()
|
||||||
|
*/
|
||||||
|
$manager->setDefaultDriver('file');
|
||||||
|
|
||||||
|
foreach ($drivers as $driver => $className) {
|
||||||
|
/** @var SessionDriverInterface $driverInstance */
|
||||||
|
$driverInstance = $container->make($className);
|
||||||
|
|
||||||
|
$manager->extend($driver, function () use ($settings, $config, $driverInstance) {
|
||||||
|
return $driverInstance->build($settings, $config);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $manager;
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->container->alias('session', SessionManager::class);
|
||||||
|
|
||||||
|
$this->container->singleton('session.handler', function (Container $container): SessionHandlerInterface {
|
||||||
|
/** @var SessionManager $manager */
|
||||||
|
$manager = $container->make('session');
|
||||||
|
|
||||||
|
return $manager->handler();
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->container->alias('session.handler', SessionHandlerInterface::class);
|
$this->container->alias('session.handler', SessionHandlerInterface::class);
|
||||||
|
116
framework/core/tests/integration/extenders/SessionTest.php
Normal file
116
framework/core/tests/integration/extenders/SessionTest.php
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Tests\integration\extenders;
|
||||||
|
|
||||||
|
use Flarum\Extend;
|
||||||
|
use Flarum\Foundation\Config;
|
||||||
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
|
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||||
|
use Flarum\Testing\integration\TestCase;
|
||||||
|
use Flarum\User\SessionDriverInterface;
|
||||||
|
use Illuminate\Session\FileSessionHandler;
|
||||||
|
use Illuminate\Session\NullSessionHandler;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use SessionHandlerInterface;
|
||||||
|
|
||||||
|
class SessionTest extends TestCase
|
||||||
|
{
|
||||||
|
use RetrievesAuthorizedUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function default_driver_exists_by_default()
|
||||||
|
{
|
||||||
|
$this->expectNotToPerformAssertions();
|
||||||
|
$this->app()->getContainer()->make('session.handler');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_driver_doesnt_exist_by_default()
|
||||||
|
{
|
||||||
|
$this->expectException(InvalidArgumentException::class);
|
||||||
|
$this->app()->getContainer()->make('session')->driver('flarum-acme');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_driver_exists_if_added()
|
||||||
|
{
|
||||||
|
$this->extend((new Extend\Session())->driver('flarum-acme', AcmeSessionDriver::class));
|
||||||
|
|
||||||
|
$driver = $this->app()->getContainer()->make('session')->driver('flarum-acme');
|
||||||
|
|
||||||
|
$this->assertEquals(NullSessionHandler::class, get_class($driver->getHandler()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_driver_overrides_laravel_defined_drivers_if_added()
|
||||||
|
{
|
||||||
|
$this->extend((new Extend\Session())->driver('redis', AcmeSessionDriver::class));
|
||||||
|
|
||||||
|
$driver = $this->app()->getContainer()->make('session')->driver('redis');
|
||||||
|
|
||||||
|
$this->assertEquals(NullSessionHandler::class, get_class($driver->getHandler()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function uses_default_driver_if_driver_from_config_file_not_configured()
|
||||||
|
{
|
||||||
|
$this->config('session.driver', null);
|
||||||
|
|
||||||
|
$handler = $this->app()->getContainer()->make('session.handler');
|
||||||
|
|
||||||
|
$this->assertEquals(FileSessionHandler::class, get_class($handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function uses_default_driver_if_configured_driver_from_config_file_unavailable()
|
||||||
|
{
|
||||||
|
$this->config('session.driver', 'nevergonnagiveyouup');
|
||||||
|
|
||||||
|
$handler = $this->app()->getContainer()->make('session.handler');
|
||||||
|
|
||||||
|
$this->assertEquals(FileSessionHandler::class, get_class($handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function uses_custom_driver_from_config_file_if_configured_and_available()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\Session)->driver('flarum-acme', AcmeSessionDriver::class)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->config('session.driver', 'flarum-acme');
|
||||||
|
|
||||||
|
$handler = $this->app()->getContainer()->make('session.handler');
|
||||||
|
|
||||||
|
$this->assertEquals(NullSessionHandler::class, get_class($handler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AcmeSessionDriver implements SessionDriverInterface
|
||||||
|
{
|
||||||
|
public function build(SettingsRepositoryInterface $settings, Config $config): SessionHandlerInterface
|
||||||
|
{
|
||||||
|
return new NullSessionHandler();
|
||||||
|
}
|
||||||
|
}
|
@ -148,7 +148,7 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
|
|||||||
*/
|
*/
|
||||||
protected function config(string $key, $value)
|
protected function config(string $key, $value)
|
||||||
{
|
{
|
||||||
$this->config[$key] = $value;
|
Arr::set($this->config, $key, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user