1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2019 webtrees development team 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16declare(strict_types=1); 17 18namespace Fisharebest\Webtrees; 19 20use Illuminate\Support\Str; 21use Psr\Http\Message\ServerRequestInterface; 22use function array_map; 23use function explode; 24use function implode; 25use function parse_url; 26use function session_name; 27use function session_regenerate_id; 28use function session_register_shutdown; 29use function session_set_cookie_params; 30use function session_set_save_handler; 31use function session_start; 32use function session_status; 33use const PHP_URL_HOST; 34use const PHP_URL_PATH; 35use const PHP_URL_SCHEME; 36 37/** 38 * Session handling 39 */ 40class Session 41{ 42 /** 43 * Start a session 44 * 45 * @param ServerRequestInterface $request 46 * 47 * @return void 48 */ 49 public static function start(ServerRequestInterface $request): void 50 { 51 // Store sessions in the database 52 session_set_save_handler(new SessionDatabaseHandler($request)); 53 54 $url = $request->getAttribute('request_uri'); 55 $secure = parse_url($url, PHP_URL_SCHEME) === 'https'; 56 $domain = parse_url($url, PHP_URL_HOST); 57 $path = parse_url($url, PHP_URL_PATH); 58 $path = explode('index.php', $path)[0]; 59 60 // Paths containing UTF-8 characters need special handling. 61 $path = implode('/', array_map('rawurlencode', explode('/', $path))); 62 63 session_name('WT_SESSION'); 64 session_register_shutdown(); 65 session_set_cookie_params(0, $path, $domain, $secure, true); 66 session_start(); 67 68 // A new session? Prevent session fixation attacks by choosing a new session ID. 69 if (!self::get('initiated')) { 70 self::regenerate(true); 71 self::put('initiated', true); 72 } 73 } 74 75 /** 76 * Read a value from the session 77 * 78 * @param string $name 79 * @param mixed $default 80 * 81 * @return mixed 82 */ 83 public static function get(string $name, $default = null) 84 { 85 return $_SESSION[$name] ?? $default; 86 } 87 88 /** 89 * After any change in authentication level, we should use a new session ID. 90 * 91 * @param bool $destroy 92 * 93 * @return void 94 */ 95 public static function regenerate(bool $destroy = false): void 96 { 97 if ($destroy) { 98 self::clear(); 99 } 100 101 if (session_status() === PHP_SESSION_ACTIVE) { 102 session_regenerate_id($destroy); 103 } 104 } 105 106 /** 107 * Remove all stored data from the session. 108 * 109 * @return void 110 */ 111 public static function clear(): void 112 { 113 $_SESSION = []; 114 } 115 116 /** 117 * Write a value to the session 118 * 119 * @param string $name 120 * @param mixed $value 121 * 122 * @return void 123 */ 124 public static function put(string $name, $value): void 125 { 126 $_SESSION[$name] = $value; 127 } 128 129 /** 130 * Remove a value from the session 131 * 132 * @param string $name 133 * 134 * @return void 135 */ 136 public static function forget(string $name): void 137 { 138 unset($_SESSION[$name]); 139 } 140 141 /** 142 * Cross-Site Request Forgery tokens - ensure that the user is submitting 143 * a form that was generated by the current session. 144 * 145 * @return string 146 */ 147 public static function getCsrfToken(): string 148 { 149 if (!self::has('CSRF_TOKEN')) { 150 self::put('CSRF_TOKEN', Str::random(32)); 151 } 152 153 return self::get('CSRF_TOKEN'); 154 } 155 156 /** 157 * Does a session variable exist? 158 * 159 * @param string $name 160 * 161 * @return bool 162 */ 163 public static function has(string $name): bool 164 { 165 return isset($_SESSION[$name]); 166 } 167} 168