Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions framework/core/src/Extend/Session.php
Original file line number Diff line number Diff line change
@@ -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);
});
}
}
43 changes: 42 additions & 1 deletion framework/core/src/Foundation/Console/InfoCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@
use Flarum\Foundation\Application;
use Flarum\Foundation\Config;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\SessionManager;
use Illuminate\Contracts\Queue\Queue;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use PDO;
use SessionHandlerInterface;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableStyle;

Expand Down Expand Up @@ -47,18 +50,32 @@ class InfoCommand extends AbstractCommand
*/
private $queue;

/**
* @var SessionManager
*/
private $session;

/**
* @var SessionHandlerInterface
*/
private $sessionHandler;

public function __construct(
ExtensionManager $extensions,
Config $config,
SettingsRepositoryInterface $settings,
ConnectionInterface $db,
Queue $queue
Queue $queue,
SessionManager $session,
SessionHandlerInterface $sessionHandler
) {
$this->extensions = $extensions;
$this->config = $config;
$this->settings = $settings;
$this->db = $db;
$this->queue = $queue;
$this->session = $session;
$this->sessionHandler = $sessionHandler;

parent::__construct();
}
Expand Down Expand Up @@ -92,6 +109,7 @@ protected function fire()
$this->output->writeln('<info>Base URL:</info> '.$this->config->url());
$this->output->writeln('<info>Installation path:</info> '.getcwd());
$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>Debug mode:</info> '.($this->config->inDebugMode() ? '<error>ON</error>' : 'off'));

Expand Down Expand Up @@ -168,4 +186,27 @@ private function identifyDatabaseVersion(): string
{
return $this->db->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
}

/**
* Some extensions/packages might be overriding the session.handler binding.
* So we have to check if the driver used is configured or hardcoded.
*/
private function identifySessionDriver(): string
{
$defaultDriver = $this->session->getDefaultDriver();
$driver = Arr::get($this->config, 'session.driver', $defaultDriver);
$this->session->handler($driver);
$configuredDriver = isset($this->session->getDrivers()[$driver]) ? $driver : $defaultDriver;

// 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 queue SyncQueue, RedisQueue
$handlerName = str_replace('sessionhandler', '', $handlerName);

return $configuredDriver !== $handlerName ? "$handlerName <comment>(code override, configured to <options=bold,underscore>$configuredDriver</>)</comment>" : $configuredDriver;
Comment thread
SychO9 marked this conversation as resolved.
Outdated
}
}
27 changes: 27 additions & 0 deletions framework/core/src/User/SessionDriverInterface.php
Original file line number Diff line number Diff line change
@@ -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;
}
37 changes: 37 additions & 0 deletions framework/core/src/User/SessionManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?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\Session\Store;
use InvalidArgumentException;
use SessionHandlerInterface;

class SessionManager extends IlluminateSessionManager
{
public function handler(string $driver = null): SessionHandlerInterface
Comment thread
SychO9 marked this conversation as resolved.
Outdated
{
$config = $this->container->make(Config::class);

/** @var Store $driver */
try {
$driverInstance = parent::driver($driver ?? $config['session.driver']);
Comment thread
SychO9 marked this conversation as resolved.
Outdated
} catch (InvalidArgumentException $e) {
if (! $driver) {
$driverInstance = parent::driver($this->getDefaultDriver());
} else {
throw $e;
}
}

return $driverInstance->getHandler();
}
}
46 changes: 39 additions & 7 deletions framework/core/src/User/SessionServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
namespace Flarum\User;

use Flarum\Foundation\AbstractServiceProvider;
use Illuminate\Session\FileSessionHandler;
use Flarum\Foundation\Config;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Contracts\Container\Container;
use SessionHandlerInterface;

class SessionServiceProvider extends AbstractServiceProvider
Expand All @@ -20,12 +22,42 @@ class SessionServiceProvider extends AbstractServiceProvider
*/
public function register()
{
$this->container->singleton('session.handler', function ($container) {
return new FileSessionHandler(
$container['files'],
$container['config']['session.files'],
$container['config']['session.lifetime']
);
$this->container->singleton('flarum.session.drivers', function () {
return [];
});

$this->container->singleton('session', function (Container $container) {
Comment thread
SychO9 marked this conversation as resolved.
$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();
Comment thread
SychO9 marked this conversation as resolved.
});

$this->container->alias('session.handler', SessionHandlerInterface::class);
Expand Down
116 changes: 116 additions & 0 deletions framework/core/tests/integration/extenders/SessionTest.php
Original file line number Diff line number Diff line change
@@ -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')->handler('flarum-acme');
}

/**
* @test
*/
public function custom_driver_exists_if_added()
{
$this->extend((new Extend\Session())->driver('flarum-acme', AcmeSessionDriver::class));

$handler = $this->app()->getContainer()->make('session')->handler('flarum-acme');

$this->assertEquals(NullSessionHandler::class, get_class($handler));
}

/**
* @test
*/
public function custom_driver_overrides_laravel_defined_drivers_if_added()
{
$this->extend((new Extend\Session())->driver('redis', AcmeSessionDriver::class));

$handler = $this->app()->getContainer()->make('session')->handler('redis');

$this->assertEquals(NullSessionHandler::class, get_class($handler));
}

/**
* @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();
}
}
2 changes: 1 addition & 1 deletion php-packages/testing/src/integration/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ protected function extension(string ...$extensions)
*/
protected function config(string $key, $value)
{
$this->config[$key] = $value;
Arr::set($this->config, $key, $value);
}

/**
Expand Down