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