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 */ 17declare(strict_types=1); 18 19namespace Fisharebest\Webtrees; 20 21use Illuminate\Support\Str; 22use Psr\Http\Message\ServerRequestInterface; 23 24use function array_map; 25use function explode; 26use function implode; 27use function parse_url; 28use function session_name; 29use function session_regenerate_id; 30use function session_register_shutdown; 31use function session_set_cookie_params; 32use function session_set_save_handler; 33use function session_start; 34use function session_status; 35 36use const PHP_URL_HOST; 37use const PHP_URL_PATH; 38use const PHP_URL_SCHEME; 39 40/** 41 * Session handling 42 */ 43class Session 44{ 45 /** 46 * Start a session 47 * 48 * @param ServerRequestInterface $request 49 * 50 * @return void 51 */ 52 public static function start(ServerRequestInterface $request): void 53 { 54 // Store sessions in the database 55 session_set_save_handler(new SessionDatabaseHandler($request)); 56 57 $url = $request->getAttribute('base_url'); 58 $secure = parse_url($url, PHP_URL_SCHEME) === 'https'; 59 $domain = parse_url($url, PHP_URL_HOST); 60 $path = parse_url($url, PHP_URL_PATH) ?? ''; 61 62 // Paths containing UTF-8 characters need special handling. 63 $path = implode('/', array_map('rawurlencode', explode('/', $path))); 64 65 session_name('WT_SESSION'); 66 session_register_shutdown(); 67 session_set_cookie_params(0, $path, $domain, $secure, true); 68 session_start(); 69 70 // A new session? Prevent session fixation attacks by choosing a new session ID. 71 if (!self::get('initiated')) { 72 self::regenerate(true); 73 self::put('initiated', true); 74 } 75 } 76 77 /** 78 * Read a value from the session 79 * 80 * @param string $name 81 * @param mixed $default 82 * 83 * @return mixed 84 */ 85 public static function get(string $name, $default = null) 86 { 87 return $_SESSION[$name] ?? $default; 88 } 89 90 /** 91 * After any change in authentication level, we should use a new session ID. 92 * 93 * @param bool $destroy 94 * 95 * @return void 96 */ 97 public static function regenerate(bool $destroy = false): void 98 { 99 if ($destroy) { 100 self::clear(); 101 } 102 103 if (session_status() === PHP_SESSION_ACTIVE) { 104 session_regenerate_id($destroy); 105 } 106 } 107 108 /** 109 * Remove all stored data from the session. 110 * 111 * @return void 112 */ 113 public static function clear(): void 114 { 115 $_SESSION = []; 116 } 117 118 /** 119 * Write a value to the session 120 * 121 * @param string $name 122 * @param mixed $value 123 * 124 * @return void 125 */ 126 public static function put(string $name, $value): void 127 { 128 $_SESSION[$name] = $value; 129 } 130 131 /** 132 * Remove a value from the session 133 * 134 * @param string $name 135 * 136 * @return void 137 */ 138 public static function forget(string $name): void 139 { 140 unset($_SESSION[$name]); 141 } 142 143 /** 144 * Cross-Site Request Forgery tokens - ensure that the user is submitting 145 * a form that was generated by the current session. 146 * 147 * @return string 148 */ 149 public static function getCsrfToken(): string 150 { 151 if (!self::has('CSRF_TOKEN')) { 152 self::put('CSRF_TOKEN', Str::random(32)); 153 } 154 155 return self::get('CSRF_TOKEN'); 156 } 157 158 /** 159 * Does a session variable exist? 160 * 161 * @param string $name 162 * 163 * @return bool 164 */ 165 public static function has(string $name): bool 166 { 167 return isset($_SESSION[$name]); 168 } 169} 170