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