131bc7874SGreg Roach<?php 231bc7874SGreg Roach/** 331bc7874SGreg Roach * webtrees: online genealogy 41062a142SGreg Roach * Copyright (C) 2018 webtrees development team 531bc7874SGreg Roach * This program is free software: you can redistribute it and/or modify 631bc7874SGreg Roach * it under the terms of the GNU General Public License as published by 731bc7874SGreg Roach * the Free Software Foundation, either version 3 of the License, or 831bc7874SGreg Roach * (at your option) any later version. 931bc7874SGreg Roach * This program is distributed in the hope that it will be useful, 1031bc7874SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 1131bc7874SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1231bc7874SGreg Roach * GNU General Public License for more details. 1331bc7874SGreg Roach * You should have received a copy of the GNU General Public License 1431bc7874SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 1531bc7874SGreg Roach */ 1676692c8bSGreg Roachnamespace Fisharebest\Webtrees; 1731bc7874SGreg Roach 184c891c40SGreg Roachuse Symfony\Component\HttpFoundation\Request; 194c891c40SGreg Roach 2031bc7874SGreg Roach/** 2142af74e7SGreg Roach * Session handling 2231bc7874SGreg Roach */ 23*c1010edaSGreg Roachclass Session 24*c1010edaSGreg Roach{ 2531bc7874SGreg Roach /** 2631bc7874SGreg Roach * Start a session 2731bc7874SGreg Roach * 2831bc7874SGreg Roach * @param array $config 2931bc7874SGreg Roach */ 30*c1010edaSGreg Roach public static function start(array $config = []) 31*c1010edaSGreg Roach { 3213abd6f3SGreg Roach $default_config = [ 339c927afbSGreg Roach 'use_cookies' => '1', 3431bc7874SGreg Roach 'name' => 'WT_SESSION', 359c927afbSGreg Roach 'cookie_lifetime' => '0', 369c927afbSGreg Roach 'gc_maxlifetime' => '7200', 379c927afbSGreg Roach 'gc_probability' => '1', 389c927afbSGreg Roach 'gc_divisor' => '100', 3931bc7874SGreg Roach 'cookie_path' => '', 409c927afbSGreg Roach 'cookie_httponly' => '1', 4113abd6f3SGreg Roach ]; 4231bc7874SGreg Roach session_register_shutdown(); 4331bc7874SGreg Roach foreach ($config + $default_config as $key => $value) { 443b8c3a1cSGreg Roach ini_set('session.' . $key, $value); 4531bc7874SGreg Roach } 4631bc7874SGreg Roach session_start(); 4731bc7874SGreg Roach } 4831bc7874SGreg Roach 4931bc7874SGreg Roach /** 5031bc7874SGreg Roach * Read a value from the session 5131bc7874SGreg Roach * 5231bc7874SGreg Roach * @param string $name 5331bc7874SGreg Roach * @param mixed $default 5431bc7874SGreg Roach * 5531bc7874SGreg Roach * @return mixed 5631bc7874SGreg Roach */ 57*c1010edaSGreg Roach public static function get($name, $default = null) 58*c1010edaSGreg Roach { 5963485653SRico Sonntag return $_SESSION[$name] ?? $default; 6031bc7874SGreg Roach } 6131bc7874SGreg Roach 6231bc7874SGreg Roach /** 6331bc7874SGreg Roach * Write a value to the session 6431bc7874SGreg Roach * 6531bc7874SGreg Roach * @param string $name 6631bc7874SGreg Roach * @param mixed $value 6731bc7874SGreg Roach */ 68*c1010edaSGreg Roach public static function put($name, $value) 69*c1010edaSGreg Roach { 7031bc7874SGreg Roach $_SESSION[$name] = $value; 7131bc7874SGreg Roach } 7231bc7874SGreg Roach 7331bc7874SGreg Roach /** 7431bc7874SGreg Roach * Remove a value from the session 7531bc7874SGreg Roach * 7631bc7874SGreg Roach * @param string $name 7731bc7874SGreg Roach */ 78*c1010edaSGreg Roach public static function forget($name) 79*c1010edaSGreg Roach { 8031bc7874SGreg Roach unset($_SESSION[$name]); 8131bc7874SGreg Roach } 8231bc7874SGreg Roach 8331bc7874SGreg Roach /** 8431bc7874SGreg Roach * Does a session variable exist? 8531bc7874SGreg Roach * 8631bc7874SGreg Roach * @param string $name 8731bc7874SGreg Roach * 88cbc1590aSGreg Roach * @return bool 8931bc7874SGreg Roach */ 90*c1010edaSGreg Roach public static function has($name) 91*c1010edaSGreg Roach { 9291fb15f0SGreg Roach return isset($_SESSION[$name]); 9331bc7874SGreg Roach } 9431bc7874SGreg Roach 9531bc7874SGreg Roach /** 96f5004097SGreg Roach * Remove all stored data from the session. 97f5004097SGreg Roach */ 98*c1010edaSGreg Roach public static function clear() 99*c1010edaSGreg Roach { 10013abd6f3SGreg Roach $_SESSION = []; 101f5004097SGreg Roach } 102f5004097SGreg Roach 103f5004097SGreg Roach /** 10431bc7874SGreg Roach * After any change in authentication level, we should use a new session ID. 10531bc7874SGreg Roach * 10631bc7874SGreg Roach * @param bool $destroy 10731bc7874SGreg Roach */ 108*c1010edaSGreg Roach public static function regenerate($destroy = false) 109*c1010edaSGreg Roach { 110f5004097SGreg Roach if ($destroy) { 111f5004097SGreg Roach self::clear(); 112f5004097SGreg Roach } 11331bc7874SGreg Roach session_regenerate_id($destroy); 11431bc7874SGreg Roach } 11531bc7874SGreg Roach 11631bc7874SGreg Roach /** 11731bc7874SGreg Roach * Set an explicit session ID. Typically used for search robots. 11831bc7874SGreg Roach * 11931bc7874SGreg Roach * @param string $id 12031bc7874SGreg Roach */ 121*c1010edaSGreg Roach public static function setId($id) 122*c1010edaSGreg Roach { 12331bc7874SGreg Roach session_id($id); 12431bc7874SGreg Roach } 12557514a4fSGreg Roach 12657514a4fSGreg Roach /** 12757514a4fSGreg Roach * Initialise our session save handler 12857514a4fSGreg Roach */ 129*c1010edaSGreg Roach public static function setSaveHandler() 130*c1010edaSGreg Roach { 13157514a4fSGreg Roach session_set_save_handler( 13257514a4fSGreg Roach function (): bool { 13357514a4fSGreg Roach return Session::open(); 13457514a4fSGreg Roach }, 13557514a4fSGreg Roach function (): bool { 13657514a4fSGreg Roach return Session::close(); 13757514a4fSGreg Roach }, 13857514a4fSGreg Roach function (string $id): string { 13957514a4fSGreg Roach return Session::read($id); 14057514a4fSGreg Roach }, 14157514a4fSGreg Roach function (string $id, string $data): bool { 14257514a4fSGreg Roach return Session::write($id, $data); 14357514a4fSGreg Roach }, 14457514a4fSGreg Roach function (string $id): bool { 14557514a4fSGreg Roach return Session::destroy($id); 14657514a4fSGreg Roach }, 14757514a4fSGreg Roach function (int $maxlifetime): bool { 14857514a4fSGreg Roach return Session::gc($maxlifetime); 14957514a4fSGreg Roach } 15057514a4fSGreg Roach ); 15157514a4fSGreg Roach } 15257514a4fSGreg Roach 15357514a4fSGreg Roach /** 15457514a4fSGreg Roach * For session_set_save_handler() 15557514a4fSGreg Roach * 15657514a4fSGreg Roach * @return bool 15757514a4fSGreg Roach */ 158*c1010edaSGreg Roach private static function close() 159*c1010edaSGreg Roach { 16057514a4fSGreg Roach return true; 16157514a4fSGreg Roach } 16257514a4fSGreg Roach 16357514a4fSGreg Roach /** 16457514a4fSGreg Roach * For session_set_save_handler() 16557514a4fSGreg Roach * 16657514a4fSGreg Roach * @param string $id 16757514a4fSGreg Roach * 16857514a4fSGreg Roach * @return bool 16957514a4fSGreg Roach */ 170*c1010edaSGreg Roach private static function destroy(string $id) 171*c1010edaSGreg Roach { 17257514a4fSGreg Roach Database::prepare( 17357514a4fSGreg Roach "DELETE FROM `##session` WHERE session_id = :session_id" 17457514a4fSGreg Roach )->execute([ 175*c1010edaSGreg Roach 'session_id' => $id, 17657514a4fSGreg Roach ]); 17757514a4fSGreg Roach 17857514a4fSGreg Roach return true; 17957514a4fSGreg Roach } 18057514a4fSGreg Roach 18157514a4fSGreg Roach /** 18257514a4fSGreg Roach * For session_set_save_handler() 18357514a4fSGreg Roach * 18457514a4fSGreg Roach * @param int $maxlifetime 18557514a4fSGreg Roach * 18657514a4fSGreg Roach * @return bool 18757514a4fSGreg Roach */ 188*c1010edaSGreg Roach private static function gc(int $maxlifetime) 189*c1010edaSGreg Roach { 19057514a4fSGreg Roach Database::prepare( 19157514a4fSGreg Roach "DELETE FROM `##session` WHERE session_time < DATE_SUB(NOW(), INTERVAL :maxlifetime SECOND)" 19257514a4fSGreg Roach )->execute([ 193*c1010edaSGreg Roach 'maxlifetime' => $maxlifetime, 19457514a4fSGreg Roach ]); 19557514a4fSGreg Roach 19657514a4fSGreg Roach return true; 19757514a4fSGreg Roach } 19857514a4fSGreg Roach 19957514a4fSGreg Roach /** 20057514a4fSGreg Roach * For session_set_save_handler() 20157514a4fSGreg Roach * 20257514a4fSGreg Roach * @return bool 20357514a4fSGreg Roach */ 204*c1010edaSGreg Roach private static function open() 205*c1010edaSGreg Roach { 20657514a4fSGreg Roach return true; 20757514a4fSGreg Roach } 20857514a4fSGreg Roach 20957514a4fSGreg Roach /** 21057514a4fSGreg Roach * For session_set_save_handler() 21157514a4fSGreg Roach * 21257514a4fSGreg Roach * @param string $id 21357514a4fSGreg Roach * 21457514a4fSGreg Roach * @return string 21557514a4fSGreg Roach */ 216*c1010edaSGreg Roach private static function read(string $id): string 217*c1010edaSGreg Roach { 21857514a4fSGreg Roach return (string)Database::prepare( 21957514a4fSGreg Roach "SELECT session_data FROM `##session` WHERE session_id = :session_id" 22057514a4fSGreg Roach )->execute([ 221*c1010edaSGreg Roach 'session_id' => $id, 22257514a4fSGreg Roach ])->fetchOne(); 22357514a4fSGreg Roach } 22457514a4fSGreg Roach 22557514a4fSGreg Roach /** 22657514a4fSGreg Roach * For session_set_save_handler() 22757514a4fSGreg Roach * 22857514a4fSGreg Roach * @param string $id 22957514a4fSGreg Roach * @param string $data 23057514a4fSGreg Roach * 23157514a4fSGreg Roach * @return bool 23257514a4fSGreg Roach */ 233*c1010edaSGreg Roach private static function write(string $id, string $data): bool 234*c1010edaSGreg Roach { 2354c891c40SGreg Roach $request = Request::createFromGlobals(); 2364c891c40SGreg Roach 23757514a4fSGreg Roach // Only update the session table once per minute, unless the session data has actually changed. 23857514a4fSGreg Roach Database::prepare( 23957514a4fSGreg Roach "INSERT INTO `##session` (session_id, user_id, ip_address, session_data, session_time)" . 2404c891c40SGreg Roach " VALUES (:session_id, :user_id, :ip_address, :data, CURRENT_TIMESTAMP - SECOND(CURRENT_TIMESTAMP))" . 24157514a4fSGreg Roach " ON DUPLICATE KEY UPDATE" . 24257514a4fSGreg Roach " user_id = VALUES(user_id)," . 24357514a4fSGreg Roach " ip_address = VALUES(ip_address)," . 24457514a4fSGreg Roach " session_data = VALUES(session_data)," . 24557514a4fSGreg Roach " session_time = CURRENT_TIMESTAMP - SECOND(CURRENT_TIMESTAMP)" 24657514a4fSGreg Roach )->execute([ 2474c891c40SGreg Roach 'session_id' => $id, 2484c891c40SGreg Roach 'user_id' => (int)Auth::id(), 2494c891c40SGreg Roach 'ip_address' => $request->getClientIp(), 2504c891c40SGreg Roach 'data' => $data, 2514c891c40SGreg Roach ]); 25257514a4fSGreg Roach 25357514a4fSGreg Roach return true; 25457514a4fSGreg Roach } 25531bc7874SGreg Roach} 256