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