diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:39:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:39:39 +0000 |
commit | 8ca6cc32b2c789a3149861159ad258f2cb9491e3 (patch) | |
tree | 2492de6f1528dd44eaa169a5c1555026d9cb75ec /library/Icinga/Web/Session | |
parent | Initial commit. (diff) | |
download | icingaweb2-8ca6cc32b2c789a3149861159ad258f2cb9491e3.tar.xz icingaweb2-8ca6cc32b2c789a3149861159ad258f2cb9491e3.zip |
Adding upstream version 2.11.4.upstream/2.11.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/Icinga/Web/Session')
-rw-r--r-- | library/Icinga/Web/Session/Php72Session.php | 37 | ||||
-rw-r--r-- | library/Icinga/Web/Session/PhpSession.php | 256 | ||||
-rw-r--r-- | library/Icinga/Web/Session/Session.php | 126 | ||||
-rw-r--r-- | library/Icinga/Web/Session/SessionNamespace.php | 201 |
4 files changed, 620 insertions, 0 deletions
diff --git a/library/Icinga/Web/Session/Php72Session.php b/library/Icinga/Web/Session/Php72Session.php new file mode 100644 index 0000000..e6a6b19 --- /dev/null +++ b/library/Icinga/Web/Session/Php72Session.php @@ -0,0 +1,37 @@ +<?php +/* Icinga Web 2 | (c) 2017 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 Php72Session extends PhpSession +{ + /** + * Open a PHP session + */ + protected function open() + { + session_name($this->sessionName); + + $cookie = new Cookie('bogus'); + session_set_cookie_params( + 0, + $cookie->getPath(), + $cookie->getDomain(), + $cookie->isSecure(), + true + ); + + session_start(array( + 'use_cookies' => true, + 'use_only_cookies' => true, + 'use_trans_sid' => false + )); + } +} 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]); + } +} diff --git a/library/Icinga/Web/Session/Session.php b/library/Icinga/Web/Session/Session.php new file mode 100644 index 0000000..e73e9b4 --- /dev/null +++ b/library/Icinga/Web/Session/Session.php @@ -0,0 +1,126 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Web\Session; + +use Icinga\Exception\NotImplementedError; + +/** + * Base class for handling sessions + */ +abstract class Session extends SessionNamespace +{ + /** + * Container for session namespaces + * + * @var array + */ + protected $namespaces = array(); + + /** + * The identifiers of all namespaces removed from this session + * + * @var array + */ + protected $removedNamespaces = array(); + + /** + * Read all values from the underlying session implementation + */ + abstract public function read(); + + /** + * Persists changes to the underlying session implementation + */ + public function write() + { + throw new NotImplementedError('You are required to implement write() in your session implementation'); + } + + /** + * Return whether a session exists + * + * @return bool + */ + abstract public function exists(); + + /** + * Purge session + */ + abstract public function purge(); + + /** + * Assign a new session id to this session. + */ + abstract public function refreshId(); + + /** + * Return the id of this session + * + * @return string + */ + abstract public function getId(); + + /** + * Get or create a new session namespace + * + * @param string $identifier The namespace's identifier + * + * @return SessionNamespace + */ + public function getNamespace($identifier) + { + if (!isset($this->namespaces[$identifier])) { + if (in_array($identifier, $this->removedNamespaces, true)) { + unset($this->removedNamespaces[array_search($identifier, $this->removedNamespaces, true)]); + } + + $this->namespaces[$identifier] = new SessionNamespace(); + } + + return $this->namespaces[$identifier]; + } + + /** + * Return whether the given session namespace exists + * + * @param string $identifier The namespace's identifier to check + * + * @return bool + */ + public function hasNamespace($identifier) + { + return isset($this->namespaces[$identifier]); + } + + /** + * Remove the given session namespace + * + * @param string $identifier The identifier of the namespace to remove + */ + public function removeNamespace($identifier) + { + unset($this->namespaces[$identifier]); + $this->removedNamespaces[] = $identifier; + } + + /** + * Return whether the session has changed + * + * @return bool + */ + public function hasChanged() + { + return parent::hasChanged() || false === empty($this->namespaces) || false === empty($this->removedNamespaces); + } + + /** + * Clear all values and namespaces from the session cache + */ + public function clear() + { + parent::clear(); + $this->namespaces = array(); + $this->removedNamespaces = array(); + } +} diff --git a/library/Icinga/Web/Session/SessionNamespace.php b/library/Icinga/Web/Session/SessionNamespace.php new file mode 100644 index 0000000..1c9c13f --- /dev/null +++ b/library/Icinga/Web/Session/SessionNamespace.php @@ -0,0 +1,201 @@ +<?php +/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Web\Session; + +use Exception; +use ArrayIterator; +use Icinga\Exception\IcingaException; +use IteratorAggregate; +use Traversable; + +/** + * Container for session values + */ +class SessionNamespace implements IteratorAggregate +{ + /** + * The actual values stored in this container + * + * @var array + */ + protected $values = array(); + + /** + * The names of all values removed from this container + * + * @var array + */ + protected $removed = array(); + + /** + * Return an iterator for all values in this namespace + * + * @return ArrayIterator + */ + public function getIterator(): Traversable + { + return new ArrayIterator($this->getAll()); + } + + /** + * Set a session value by property access + * + * @param string $key The value's name + * @param mixed $value The value + */ + public function __set($key, $value) + { + $this->set($key, $value); + } + + /** + * Return a session value by property access + * + * @param string $key The value's name + * + * @return mixed The value + * @throws Exception When the given value-name is not found + */ + public function __get($key) + { + if (!array_key_exists($key, $this->values)) { + throw new IcingaException( + 'Cannot access non-existent session value "%s"', + $key + ); + } + + return $this->get($key); + } + + /** + * Return whether the given session value is set + * + * @param string $key The value's name + * @return bool + */ + public function __isset($key) + { + return isset($this->values[$key]); + } + + /** + * Unset the given session value + * + * @param string $key The value's name + */ + public function __unset($key) + { + $this->delete($key); + } + + /** + * Setter for session values + * + * @param string $key Name of value + * @param mixed $value Value to set + * + * @return $this + */ + public function set($key, $value) + { + $this->values[$key] = $value; + + if (in_array($key, $this->removed, true)) { + unset($this->removed[array_search($key, $this->removed, true)]); + } + + return $this; + } + + public function setByRef($key, &$value) + { + $this->values[$key] = & $value; + + if (in_array($key, $this->removed, true)) { + unset($this->removed[array_search($key, $this->removed, true)]); + } + + return $this; + } + + /** + * Getter for session values + * + * @param string $key Name of the value to return + * @param mixed $default Default value to return + * + * @return mixed + */ + public function get($key, $default = null) + { + return isset($this->values[$key]) ? $this->values[$key] : $default; + } + + public function & getByRef($key, $default = null) + { + $value = $default; + if (isset($this->values[$key])) { + $value = & $this->values[$key]; + } + + return $value; + } + + /** + * Delete the given value from the session + * + * @param string $key The value's name + */ + public function delete($key) + { + $this->removed[] = $key; + unset($this->values[$key]); + } + + /** + * Getter for all session values + * + * @return array + */ + public function getAll() + { + return $this->values; + } + + /** + * Put an array into the session + * + * @param array $values Values to set + * @param bool $overwrite Overwrite existing values + */ + public function setAll(array $values, $overwrite = false) + { + foreach ($values as $key => $value) { + if ($this->get($key, $value) !== $value && !$overwrite) { + continue; + } + $this->set($key, $value); + } + } + + /** + * Return whether the session namespace has been changed + * + * @return bool + */ + public function hasChanged() + { + return false === empty($this->values) || false === empty($this->removed); + } + + /** + * Clear all values from the session namespace + */ + public function clear() + { + $this->values = array(); + $this->removed = array(); + } +} |