361 lines
10 KiB
PHP
361 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* Joomla! Content Management System
|
|
*
|
|
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
|
|
* @license GNU General Public License version 2 or later; see LICENSE
|
|
*/
|
|
|
|
namespace Joomla\CMS\Service\Provider;
|
|
|
|
\defined('JPATH_PLATFORM') or die;
|
|
|
|
use Joomla\CMS\Application\AdministratorApplication;
|
|
use Joomla\CMS\Application\ApplicationHelper;
|
|
use Joomla\CMS\Application\CMSApplicationInterface;
|
|
use Joomla\CMS\Application\ConsoleApplication;
|
|
use Joomla\CMS\Application\SiteApplication;
|
|
use Joomla\CMS\Factory;
|
|
use Joomla\CMS\Installation\Application\InstallationApplication;
|
|
use Joomla\CMS\Session\EventListener\MetadataManagerListener;
|
|
use Joomla\CMS\Session\MetadataManager;
|
|
use Joomla\CMS\Session\SessionFactory;
|
|
use Joomla\CMS\Session\SessionManager;
|
|
use Joomla\CMS\Session\Storage\JoomlaStorage;
|
|
use Joomla\Database\DatabaseInterface;
|
|
use Joomla\DI\Container;
|
|
use Joomla\DI\Exception\DependencyResolutionException;
|
|
use Joomla\DI\ServiceProviderInterface;
|
|
use Joomla\Event\DispatcherInterface;
|
|
use Joomla\Event\LazyServiceEventListener;
|
|
use Joomla\Event\Priority;
|
|
use Joomla\Registry\Registry;
|
|
use Joomla\Session\HandlerInterface;
|
|
use Joomla\Session\SessionEvents;
|
|
use Joomla\Session\SessionInterface;
|
|
use Joomla\Session\Storage\RuntimeStorage;
|
|
use Joomla\Session\StorageInterface;
|
|
|
|
/**
|
|
* Service provider for the application's session dependency
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
class Session implements ServiceProviderInterface
|
|
{
|
|
/**
|
|
* Registers the service provider with a DI container.
|
|
*
|
|
* @param Container $container The DI container.
|
|
*
|
|
* @return void
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
public function register(Container $container)
|
|
{
|
|
$container->share(
|
|
'session.web.administrator',
|
|
function (Container $container)
|
|
{
|
|
/** @var Registry $config */
|
|
$config = $container->get('config');
|
|
$app = Factory::getApplication();
|
|
|
|
// Generate a session name.
|
|
$name = ApplicationHelper::getHash($config->get('session_name', AdministratorApplication::class));
|
|
|
|
// Calculate the session lifetime.
|
|
$lifetime = $config->get('lifetime') ? $config->get('lifetime') * 60 : 900;
|
|
|
|
// Initialize the options for the Session object.
|
|
$options = [
|
|
'name' => $name,
|
|
'expire' => $lifetime,
|
|
];
|
|
|
|
if ($config->get('force_ssl') >= 1)
|
|
{
|
|
$options['force_ssl'] = true;
|
|
}
|
|
|
|
$handler = $container->get('session.factory')->createSessionHandler($options);
|
|
|
|
if (!$container->has('session.handler'))
|
|
{
|
|
$this->registerSessionHandlerAsService($container, $handler);
|
|
}
|
|
|
|
return $this->buildSession(
|
|
new JoomlaStorage($app->input, $handler),
|
|
$app,
|
|
$container->get(DispatcherInterface::class),
|
|
$options
|
|
);
|
|
},
|
|
true
|
|
);
|
|
|
|
$container->share(
|
|
'session.web.installation',
|
|
function (Container $container)
|
|
{
|
|
/** @var Registry $config */
|
|
$config = $container->get('config');
|
|
$app = Factory::getApplication();
|
|
|
|
/**
|
|
* Session handler for the session is always filesystem so it doesn't flip to the database after
|
|
* configuration.php has been written to
|
|
*/
|
|
$config->set('session_handler', 'filesystem');
|
|
|
|
/**
|
|
* Generate a session name - unlike all the other apps we don't have either a secret or a session name
|
|
* (that's not the app name) until we complete installation which then leads to us dropping things like
|
|
* language preferences after installation as the app refreshes.
|
|
*/
|
|
$name = md5(serialize(JPATH_ROOT . InstallationApplication::class));
|
|
|
|
// Calculate the session lifetime.
|
|
$lifetime = $config->get('lifetime') ? $config->get('lifetime') * 60 : 900;
|
|
|
|
// Initialize the options for the Session object.
|
|
$options = [
|
|
'name' => $name,
|
|
'expire' => $lifetime,
|
|
];
|
|
|
|
$handler = $container->get('session.factory')->createSessionHandler($options);
|
|
|
|
if (!$container->has('session.handler'))
|
|
{
|
|
$this->registerSessionHandlerAsService($container, $handler);
|
|
}
|
|
|
|
return $this->buildSession(
|
|
new JoomlaStorage($app->input, $handler),
|
|
$app,
|
|
$container->get(DispatcherInterface::class),
|
|
$options
|
|
);
|
|
},
|
|
true
|
|
);
|
|
|
|
$container->share(
|
|
'session.web.site',
|
|
function (Container $container)
|
|
{
|
|
/** @var Registry $config */
|
|
$config = $container->get('config');
|
|
$app = Factory::getApplication();
|
|
|
|
// Generate a session name.
|
|
$name = ApplicationHelper::getHash($config->get('session_name', SiteApplication::class));
|
|
|
|
// Calculate the session lifetime.
|
|
$lifetime = $config->get('lifetime') ? $config->get('lifetime') * 60 : 900;
|
|
|
|
// Initialize the options for the Session object.
|
|
$options = [
|
|
'name' => $name,
|
|
'expire' => $lifetime,
|
|
];
|
|
|
|
if ($config->get('force_ssl') == 2)
|
|
{
|
|
$options['force_ssl'] = true;
|
|
}
|
|
|
|
$handler = $container->get('session.factory')->createSessionHandler($options);
|
|
|
|
if (!$container->has('session.handler'))
|
|
{
|
|
$this->registerSessionHandlerAsService($container, $handler);
|
|
}
|
|
|
|
return $this->buildSession(
|
|
new JoomlaStorage($app->input, $handler),
|
|
$app,
|
|
$container->get(DispatcherInterface::class),
|
|
$options
|
|
);
|
|
},
|
|
true
|
|
);
|
|
|
|
$container->share(
|
|
'session.cli',
|
|
function (Container $container)
|
|
{
|
|
/** @var Registry $config */
|
|
$config = $container->get('config');
|
|
$app = Factory::getApplication();
|
|
|
|
// Generate a session name.
|
|
$name = ApplicationHelper::getHash($config->get('session_name', ConsoleApplication::class));
|
|
|
|
// Calculate the session lifetime.
|
|
$lifetime = $config->get('lifetime') ? $config->get('lifetime') * 60 : 900;
|
|
|
|
// Initialize the options for the Session object.
|
|
$options = [
|
|
'name' => $name,
|
|
'expire' => $lifetime,
|
|
];
|
|
|
|
// Unlike the web apps, we will only toggle the force SSL setting based on it being enabled and not based on client
|
|
if ($config->get('force_ssl') >= 1)
|
|
{
|
|
$options['force_ssl'] = true;
|
|
}
|
|
|
|
$handler = $container->get('session.factory')->createSessionHandler($options);
|
|
|
|
if (!$container->has('session.handler'))
|
|
{
|
|
$this->registerSessionHandlerAsService($container, $handler);
|
|
}
|
|
|
|
return $this->buildSession(
|
|
new RuntimeStorage,
|
|
$app,
|
|
$container->get(DispatcherInterface::class),
|
|
$options
|
|
);
|
|
},
|
|
true
|
|
);
|
|
|
|
$container->alias(SessionFactory::class, 'session.factory')
|
|
->share(
|
|
'session.factory',
|
|
function (Container $container)
|
|
{
|
|
$factory = new SessionFactory;
|
|
$factory->setContainer($container);
|
|
|
|
return $factory;
|
|
},
|
|
true
|
|
);
|
|
|
|
$container->alias(SessionManager::class, 'session.manager')
|
|
->share(
|
|
'session.manager',
|
|
function (Container $container)
|
|
{
|
|
if (!$container->has('session.handler'))
|
|
{
|
|
throw new DependencyResolutionException(
|
|
'The "session.handler" service has not been created, make sure you have created the "session" service first.'
|
|
);
|
|
}
|
|
|
|
return new SessionManager($container->get('session.handler'));
|
|
},
|
|
true
|
|
);
|
|
|
|
$container->alias(MetadataManager::class, 'session.metadata_manager')
|
|
->share(
|
|
'session.metadata_manager',
|
|
function (Container $container)
|
|
{
|
|
/*
|
|
* Normally we should inject the application as a dependency via $container->get() however there is not
|
|
* a 'app' or CMSApplicationInterface::class key for the primary application of the request so we need to
|
|
* rely on the application having been injected to the global Factory otherwise we cannot build the service
|
|
*/
|
|
if (!Factory::$application)
|
|
{
|
|
throw new DependencyResolutionException(
|
|
sprintf(
|
|
'Creating the "session.metadata_manager" service requires %s::$application be initialised.',
|
|
Factory::class
|
|
)
|
|
);
|
|
}
|
|
|
|
return new MetadataManager(Factory::$application, $container->get(DatabaseInterface::class));
|
|
},
|
|
true
|
|
);
|
|
|
|
$container->alias(MetadataManagerListener::class, 'session.event_listener.metadata_manager')
|
|
->share(
|
|
'session.event_listener.metadata_manager',
|
|
function (Container $container)
|
|
{
|
|
return new MetadataManagerListener($container->get(MetadataManager::class), $container->get('config'));
|
|
},
|
|
true
|
|
);
|
|
|
|
$listener = new LazyServiceEventListener($container, 'session.event_listener.metadata_manager', 'onAfterSessionStart');
|
|
|
|
/** @var DispatcherInterface $dispatcher */
|
|
$dispatcher = $container->get(DispatcherInterface::class);
|
|
$dispatcher->addListener(SessionEvents::START, $listener);
|
|
}
|
|
|
|
/**
|
|
* Build the root session service
|
|
*
|
|
* @param StorageInterface $storage The session storage engine.
|
|
* @param CMSApplicationInterface $app The application instance.
|
|
* @param DispatcherInterface $dispatcher The event dispatcher.
|
|
* @param array $options The configured session options.
|
|
*
|
|
* @return SessionInterface
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
private function buildSession(
|
|
StorageInterface $storage,
|
|
CMSApplicationInterface $app,
|
|
DispatcherInterface $dispatcher,
|
|
array $options
|
|
): SessionInterface
|
|
{
|
|
$input = $app->input;
|
|
|
|
if (method_exists($app, 'afterSessionStart'))
|
|
{
|
|
$dispatcher->addListener(SessionEvents::START, [$app, 'afterSessionStart'], Priority::HIGH);
|
|
}
|
|
|
|
$session = new \Joomla\CMS\Session\Session($storage, $dispatcher, $options);
|
|
|
|
return $session;
|
|
}
|
|
|
|
/**
|
|
* Registers the session handler as a service
|
|
*
|
|
* @param Container $container The container to register the service to.
|
|
* @param \SessionHandlerInterface $sessionHandler The session handler.
|
|
*
|
|
* @return void
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
private function registerSessionHandlerAsService(Container $container, \SessionHandlerInterface $sessionHandler): void
|
|
{
|
|
// Alias the session handler to the core SessionHandlerInterface for improved autowiring and discoverability
|
|
$container->alias(\SessionHandlerInterface::class, 'session.handler')
|
|
->share(
|
|
'session.handler',
|
|
$sessionHandler,
|
|
true
|
|
);
|
|
|
|
// If the session handler implements the extended interface, register an alias for that as well
|
|
if ($sessionHandler instanceof HandlerInterface)
|
|
{
|
|
$container->alias(HandlerInterface::class, 'session.handler');
|
|
}
|
|
}
|
|
}
|