summaryrefslogtreecommitdiffstats
path: root/library/Icinga/Web/Session/PhpSession.php
diff options
context:
space:
mode:
Diffstat (limited to 'library/Icinga/Web/Session/PhpSession.php')
-rw-r--r--library/Icinga/Web/Session/PhpSession.php256
1 files changed, 256 insertions, 0 deletions
diff --git a/library/Icinga/Web/Session/PhpSession.php b/library/Icinga/Web/Session/PhpSession.php
new file mode 100644
index 0000000..36dd84e
--- /dev/null
+++ b/library/Icinga/Web/Session/PhpSession.php
@@ -0,0 +1,256 @@
+<?php
+/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Web\Session;
+
+use Icinga\Application\Logger;
+use Icinga\Exception\ConfigurationError;
+use Icinga\Web\Cookie;
+
+/**
+ * Session implementation in PHP
+ */
+class PhpSession extends Session
+{
+ /**
+ * The namespace prefix
+ *
+ * Used to differentiate between standard session keys and namespace identifiers
+ */
+ const NAMESPACE_PREFIX = 'ns.';
+
+ /**
+ * Whether the session has already been closed
+ *
+ * @var bool
+ */
+ protected $hasBeenTouched = false;
+
+ /**
+ * Name of the session
+ *
+ * @var string
+ */
+ protected $sessionName = 'Icingaweb2';
+
+ /**
+ * Create a new PHPSession object using the provided options (if any)
+ *
+ * @param array $options An optional array of ini options to set
+ *
+ * @return static
+ *
+ * @throws ConfigurationError
+ * @see http://php.net/manual/en/session.configuration.php
+ */
+ public static function create(array $options = null)
+ {
+ return version_compare(PHP_VERSION, '7.2.0') < 0 ? new self($options) : new Php72Session($options);
+ }
+
+ /**
+ * Create a new PHPSession object using the provided options (if any)
+ *
+ * @param array $options An optional array of ini options to set
+ *
+ * @throws ConfigurationError
+ * @see http://php.net/manual/en/session.configuration.php
+ */
+ public function __construct(array $options = null)
+ {
+ $defaultCookieOptions = array(
+ 'use_trans_sid' => false,
+ 'use_cookies' => true,
+ 'cookie_httponly' => true,
+ 'use_only_cookies' => true
+ );
+
+ if (version_compare(PHP_VERSION, '7.1.0') < 0) {
+ $defaultCookieOptions['hash_function'] = true;
+ $defaultCookieOptions['hash_bits_per_character'] = 5;
+ } else {
+ $defaultCookieOptions['sid_bits_per_character'] = 5;
+ }
+
+ if ($options !== null) {
+ $options = array_merge($defaultCookieOptions, $options);
+ } else {
+ $options = $defaultCookieOptions;
+ }
+
+ if (array_key_exists('test_session_name', $options)) {
+ $this->sessionName = $options['test_session_name'];
+ unset($options['test_session_name']);
+ }
+
+ foreach ($options as $sessionVar => $value) {
+ if (ini_set("session." . $sessionVar, $value) === false) {
+ Logger::warning(
+ 'Could not set php.ini setting %s = %s. This might affect your sessions behaviour.',
+ $sessionVar,
+ $value
+ );
+ }
+ }
+
+ $sessionSavePath = session_save_path() ?: sys_get_temp_dir();
+ if (session_module_name() === 'files' && !is_writable($sessionSavePath)) {
+ throw new ConfigurationError("Can't save session, path '$sessionSavePath' is not writable.");
+ }
+
+ if ($this->exists()) {
+ // We do not want to start a new session here if there is not any
+ $this->read();
+ }
+ }
+
+ /**
+ * Open a PHP session
+ */
+ protected function open()
+ {
+ session_name($this->sessionName);
+
+ if ($this->hasBeenTouched) {
+ $cacheLimiter = ini_get('session.cache_limiter');
+ ini_set('session.use_cookies', false);
+ ini_set('session.use_only_cookies', false);
+ ini_set('session.cache_limiter', null);
+ }
+
+ $cookie = new Cookie('bogus');
+ session_set_cookie_params(
+ 0,
+ $cookie->getPath(),
+ $cookie->getDomain(),
+ $cookie->isSecure(),
+ true
+ );
+
+ session_start();
+
+ if ($this->hasBeenTouched) {
+ ini_set('session.use_cookies', true);
+ ini_set('session.use_only_cookies', true);
+ /** @noinspection PhpUndefinedVariableInspection */
+ ini_set('session.cache_limiter', $cacheLimiter);
+ }
+ }
+
+ /**
+ * Read all values written to the underling session and make them accessible.
+ */
+ public function read()
+ {
+ $this->clear();
+ $this->open();
+
+ foreach ($_SESSION as $key => $value) {
+ if (strpos($key, self::NAMESPACE_PREFIX) === 0) {
+ $namespace = new SessionNamespace();
+ $namespace->setAll($value);
+ $this->namespaces[substr($key, strlen(self::NAMESPACE_PREFIX))] = $namespace;
+ } else {
+ $this->set($key, $value);
+ }
+ }
+
+ session_write_close();
+ $this->hasBeenTouched = true;
+ }
+
+ /**
+ * Write all values of this session object to the underlying session implementation
+ */
+ public function write()
+ {
+ $this->open();
+
+ foreach ($this->removed as $key) {
+ unset($_SESSION[$key]);
+ }
+ foreach ($this->values as $key => $value) {
+ $_SESSION[$key] = $value;
+ }
+ foreach ($this->removedNamespaces as $identifier) {
+ unset($_SESSION[self::NAMESPACE_PREFIX . $identifier]);
+ }
+ foreach ($this->namespaces as $identifier => $namespace) {
+ $_SESSION[self::NAMESPACE_PREFIX . $identifier] = $namespace->getAll();
+ }
+
+ session_write_close();
+ $this->hasBeenTouched = true;
+ }
+
+ /**
+ * Delete the current session, causing all session information to be lost
+ */
+ public function purge()
+ {
+ $this->open();
+ $_SESSION = array();
+ $this->clear();
+ session_destroy();
+ $this->clearCookies();
+ session_write_close();
+ $this->hasBeenTouched = true;
+ }
+
+ /**
+ * Remove session cookies
+ */
+ protected function clearCookies()
+ {
+ if (ini_get('session.use_cookies')) {
+ Logger::debug('Clear session cookie');
+ $params = session_get_cookie_params();
+ setcookie(
+ session_name(),
+ '',
+ time() - 42000,
+ $params['path'],
+ $params['domain'],
+ $params['secure'],
+ $params['httponly']
+ );
+ }
+ }
+
+ /**
+ * @see Session::getId()
+ */
+ public function getId()
+ {
+ if (($id = session_id()) === '') {
+ // Make sure we actually get a id
+ $this->open();
+ session_write_close();
+ $this->hasBeenTouched = true;
+ $id = session_id();
+ }
+
+ return $id;
+ }
+
+ /**
+ * Assign a new sessionId to the currently active session
+ */
+ public function refreshId()
+ {
+ $this->open();
+ if ($this->exists()) {
+ session_regenerate_id();
+ }
+ session_write_close();
+ $this->hasBeenTouched = true;
+ }
+
+ /**
+ * @see Session::exists()
+ */
+ public function exists()
+ {
+ return isset($_COOKIE[$this->sessionName]);
+ }
+}