xref: /webtrees/app/Session.php (revision deb4b68556c6b3e0785ddf30b3e79f64fe59fe65)
131bc7874SGreg Roach<?php
231bc7874SGreg Roach/**
331bc7874SGreg Roach * webtrees: online genealogy
48fcd0d32SGreg Roach * Copyright (C) 2019 webtrees development team
531bc7874SGreg Roach * This program is free software: you can redistribute it and/or modify
631bc7874SGreg Roach * it under the terms of the GNU General Public License as published by
731bc7874SGreg Roach * the Free Software Foundation, either version 3 of the License, or
831bc7874SGreg Roach * (at your option) any later version.
931bc7874SGreg Roach * This program is distributed in the hope that it will be useful,
1031bc7874SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
1131bc7874SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1231bc7874SGreg Roach * GNU General Public License for more details.
1331bc7874SGreg Roach * You should have received a copy of the GNU General Public License
1431bc7874SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
1531bc7874SGreg Roach */
16e7f56f2aSGreg Roachdeclare(strict_types=1);
17e7f56f2aSGreg Roach
1876692c8bSGreg Roachnamespace Fisharebest\Webtrees;
1931bc7874SGreg Roach
20fe3a9054SGreg Roachuse Carbon\Carbon;
21fe3a9054SGreg Roachuse Illuminate\Database\Capsule\Manager as DB;
22*deb4b685SGreg Roachuse Illuminate\Support\Str;
234c891c40SGreg Roachuse Symfony\Component\HttpFoundation\Request;
24fe3a9054SGreg Roachuse function session_status;
254c891c40SGreg Roach
2631bc7874SGreg Roach/**
2742af74e7SGreg Roach * Session handling
2831bc7874SGreg Roach */
29c1010edaSGreg Roachclass Session
30c1010edaSGreg Roach{
3131bc7874SGreg Roach    /**
3231bc7874SGreg Roach     * Start a session
33c7ff4153SGreg Roach     *
34c7ff4153SGreg Roach     * @return void
3531bc7874SGreg Roach     */
3608df3d18SGreg Roach    public static function start()
37c1010edaSGreg Roach    {
3808df3d18SGreg Roach        $domain   = '';
3908df3d18SGreg Roach        $path     = parse_url(WT_BASE_URL, PHP_URL_PATH);
4008df3d18SGreg Roach        $secure   = parse_url(WT_BASE_URL, PHP_URL_SCHEME) === 'https';
4108df3d18SGreg Roach        $httponly = true;
4208df3d18SGreg Roach
4308df3d18SGreg Roach        // Paths containing UTF-8 characters need special handling.
4408df3d18SGreg Roach        $path = implode('/', array_map('rawurlencode', explode('/', $path)));
4508df3d18SGreg Roach
4608df3d18SGreg Roach        self::setSaveHandler();
4708df3d18SGreg Roach
4808df3d18SGreg Roach        session_name('WT_SESSION');
4931bc7874SGreg Roach        session_register_shutdown();
50a0dfa978SGreg Roach        session_set_cookie_params(0, $path, $domain, $secure, $httponly);
5131bc7874SGreg Roach        session_start();
5208df3d18SGreg Roach
5308df3d18SGreg Roach        // A new session? Prevent session fixation attacks by choosing a new session ID.
5408df3d18SGreg Roach        if (!self::get('initiated')) {
5508df3d18SGreg Roach            self::regenerate(true);
5608df3d18SGreg Roach            self::put('initiated', true);
5708df3d18SGreg Roach        }
5831bc7874SGreg Roach    }
5931bc7874SGreg Roach
6031bc7874SGreg Roach    /**
6131bc7874SGreg Roach     * Read a value from the session
6231bc7874SGreg Roach     *
6331bc7874SGreg Roach     * @param string $name
6431bc7874SGreg Roach     * @param mixed  $default
6531bc7874SGreg Roach     *
6631bc7874SGreg Roach     * @return mixed
6731bc7874SGreg Roach     */
68c7ff4153SGreg Roach    public static function get(string $name, $default = null)
69c1010edaSGreg Roach    {
7063485653SRico Sonntag        return $_SESSION[$name] ?? $default;
7131bc7874SGreg Roach    }
7231bc7874SGreg Roach
7331bc7874SGreg Roach    /**
7431bc7874SGreg Roach     * Write a value to the session
7531bc7874SGreg Roach     *
7631bc7874SGreg Roach     * @param string $name
7731bc7874SGreg Roach     * @param mixed  $value
78c7ff4153SGreg Roach     *
79c7ff4153SGreg Roach     * @return void
8031bc7874SGreg Roach     */
81c7ff4153SGreg Roach    public static function put(string $name, $value)
82c1010edaSGreg Roach    {
8331bc7874SGreg Roach        $_SESSION[$name] = $value;
8431bc7874SGreg Roach    }
8531bc7874SGreg Roach
8631bc7874SGreg Roach    /**
8731bc7874SGreg Roach     * Remove a value from the session
8831bc7874SGreg Roach     *
8931bc7874SGreg Roach     * @param string $name
90c7ff4153SGreg Roach     *
91c7ff4153SGreg Roach     * @return void
9231bc7874SGreg Roach     */
93c7ff4153SGreg Roach    public static function forget(string $name)
94c1010edaSGreg Roach    {
9531bc7874SGreg Roach        unset($_SESSION[$name]);
9631bc7874SGreg Roach    }
9731bc7874SGreg Roach
9831bc7874SGreg Roach    /**
9931bc7874SGreg Roach     * Does a session variable exist?
10031bc7874SGreg Roach     *
10131bc7874SGreg Roach     * @param string $name
10231bc7874SGreg Roach     *
103cbc1590aSGreg Roach     * @return bool
10431bc7874SGreg Roach     */
105c7ff4153SGreg Roach    public static function has(string $name): bool
106c1010edaSGreg Roach    {
10791fb15f0SGreg Roach        return isset($_SESSION[$name]);
10831bc7874SGreg Roach    }
10931bc7874SGreg Roach
11031bc7874SGreg Roach    /**
111f5004097SGreg Roach     * Remove all stored data from the session.
112c7ff4153SGreg Roach     *
113c7ff4153SGreg Roach     * @return void
114f5004097SGreg Roach     */
115c1010edaSGreg Roach    public static function clear()
116c1010edaSGreg Roach    {
11713abd6f3SGreg Roach        $_SESSION = [];
118f5004097SGreg Roach    }
119f5004097SGreg Roach
120f5004097SGreg Roach    /**
12131bc7874SGreg Roach     * After any change in authentication level, we should use a new session ID.
12231bc7874SGreg Roach     *
12331bc7874SGreg Roach     * @param bool $destroy
124c7ff4153SGreg Roach     *
125c7ff4153SGreg Roach     * @return void
12631bc7874SGreg Roach     */
127c7ff4153SGreg Roach    public static function regenerate(bool $destroy = false)
128c1010edaSGreg Roach    {
129f5004097SGreg Roach        if ($destroy) {
130f5004097SGreg Roach            self::clear();
131f5004097SGreg Roach        }
13201461f86SGreg Roach
13301461f86SGreg Roach        if (session_status() === PHP_SESSION_ACTIVE) {
13431bc7874SGreg Roach            session_regenerate_id($destroy);
13531bc7874SGreg Roach        }
13601461f86SGreg Roach    }
13731bc7874SGreg Roach
13831bc7874SGreg Roach    /**
13931bc7874SGreg Roach     * Set an explicit session ID. Typically used for search robots.
14031bc7874SGreg Roach     *
14131bc7874SGreg Roach     * @param string $id
142c7ff4153SGreg Roach     *
143c7ff4153SGreg Roach     * @return void
14431bc7874SGreg Roach     */
145c7ff4153SGreg Roach    public static function setId(string $id)
146c1010edaSGreg Roach    {
14731bc7874SGreg Roach        session_id($id);
14831bc7874SGreg Roach    }
14957514a4fSGreg Roach
15057514a4fSGreg Roach    /**
15157514a4fSGreg Roach     * Initialise our session save handler
152c7ff4153SGreg Roach     *
153c7ff4153SGreg Roach     * @return void
15457514a4fSGreg Roach     */
15508df3d18SGreg Roach    private static function setSaveHandler()
156c1010edaSGreg Roach    {
15757514a4fSGreg Roach        session_set_save_handler(
15857514a4fSGreg Roach            function (): bool {
15957514a4fSGreg Roach                return Session::open();
16057514a4fSGreg Roach            },
16157514a4fSGreg Roach            function (): bool {
16257514a4fSGreg Roach                return Session::close();
16357514a4fSGreg Roach            },
16457514a4fSGreg Roach            function (string $id): string {
16557514a4fSGreg Roach                return Session::read($id);
16657514a4fSGreg Roach            },
16757514a4fSGreg Roach            function (string $id, string $data): bool {
16857514a4fSGreg Roach                return Session::write($id, $data);
16957514a4fSGreg Roach            },
17057514a4fSGreg Roach            function (string $id): bool {
17157514a4fSGreg Roach                return Session::destroy($id);
17257514a4fSGreg Roach            },
17357514a4fSGreg Roach            function (int $maxlifetime): bool {
17457514a4fSGreg Roach                return Session::gc($maxlifetime);
17557514a4fSGreg Roach            }
17657514a4fSGreg Roach        );
17757514a4fSGreg Roach    }
17857514a4fSGreg Roach
17957514a4fSGreg Roach    /**
18057514a4fSGreg Roach     * For session_set_save_handler()
18157514a4fSGreg Roach     *
18257514a4fSGreg Roach     * @return bool
18357514a4fSGreg Roach     */
1848f53f488SRico Sonntag    private static function close(): bool
185c1010edaSGreg Roach    {
18657514a4fSGreg Roach        return true;
18757514a4fSGreg Roach    }
18857514a4fSGreg Roach
18957514a4fSGreg Roach    /**
19057514a4fSGreg Roach     * For session_set_save_handler()
19157514a4fSGreg Roach     *
19257514a4fSGreg Roach     * @param string $id
19357514a4fSGreg Roach     *
19457514a4fSGreg Roach     * @return bool
19557514a4fSGreg Roach     */
1968f53f488SRico Sonntag    private static function destroy(string $id): bool
197c1010edaSGreg Roach    {
198fe3a9054SGreg Roach        DB::table('session')
199fe3a9054SGreg Roach            ->where('session_id', '=', $id)
200fe3a9054SGreg Roach            ->delete();
20157514a4fSGreg Roach
20257514a4fSGreg Roach        return true;
20357514a4fSGreg Roach    }
20457514a4fSGreg Roach
20557514a4fSGreg Roach    /**
20657514a4fSGreg Roach     * For session_set_save_handler()
20757514a4fSGreg Roach     *
20857514a4fSGreg Roach     * @param int $maxlifetime
20957514a4fSGreg Roach     *
21057514a4fSGreg Roach     * @return bool
21157514a4fSGreg Roach     */
2128f53f488SRico Sonntag    private static function gc(int $maxlifetime): bool
213c1010edaSGreg Roach    {
214fe3a9054SGreg Roach        DB::table('session')
215fe3a9054SGreg Roach            ->where('session_time', '<', Carbon::now()->subSeconds($maxlifetime))
216fe3a9054SGreg Roach            ->delete();
21757514a4fSGreg Roach
21857514a4fSGreg Roach        return true;
21957514a4fSGreg Roach    }
22057514a4fSGreg Roach
22157514a4fSGreg Roach    /**
22257514a4fSGreg Roach     * For session_set_save_handler()
22357514a4fSGreg Roach     *
22457514a4fSGreg Roach     * @return bool
22557514a4fSGreg Roach     */
2268f53f488SRico Sonntag    private static function open(): bool
227c1010edaSGreg Roach    {
22857514a4fSGreg Roach        return true;
22957514a4fSGreg Roach    }
23057514a4fSGreg Roach
23157514a4fSGreg Roach    /**
23257514a4fSGreg Roach     * For session_set_save_handler()
23357514a4fSGreg Roach     *
23457514a4fSGreg Roach     * @param string $id
23557514a4fSGreg Roach     *
23657514a4fSGreg Roach     * @return string
23757514a4fSGreg Roach     */
238c1010edaSGreg Roach    private static function read(string $id): string
239c1010edaSGreg Roach    {
240fe3a9054SGreg Roach        return (string) DB::table('session')
241fe3a9054SGreg Roach            ->where('session_id', '=', $id)
242fe3a9054SGreg Roach            ->value('session_data');
24357514a4fSGreg Roach    }
24457514a4fSGreg Roach
24557514a4fSGreg Roach    /**
24657514a4fSGreg Roach     * For session_set_save_handler()
24757514a4fSGreg Roach     *
24857514a4fSGreg Roach     * @param string $id
24957514a4fSGreg Roach     * @param string $data
25057514a4fSGreg Roach     *
25157514a4fSGreg Roach     * @return bool
25257514a4fSGreg Roach     */
253c1010edaSGreg Roach    private static function write(string $id, string $data): bool
254c1010edaSGreg Roach    {
2554c891c40SGreg Roach        $request = Request::createFromGlobals();
2564c891c40SGreg Roach
257fe3a9054SGreg Roach        DB::table('session')->updateOrInsert([
2584c891c40SGreg Roach            'session_id' => $id,
259fe3a9054SGreg Roach        ], [
260fe3a9054SGreg Roach            'session_time' => Carbon::now(),
2614c891c40SGreg Roach            'user_id'      => (int) Auth::id(),
2624c891c40SGreg Roach            'ip_address'   => $request->getClientIp(),
263fe3a9054SGreg Roach            'session_data' => $data,
2644c891c40SGreg Roach        ]);
26557514a4fSGreg Roach
26657514a4fSGreg Roach        return true;
26757514a4fSGreg Roach    }
268a45f9889SGreg Roach
269a45f9889SGreg Roach    /**
270a45f9889SGreg Roach     * Cross-Site Request Forgery tokens - ensure that the user is submitting
271a45f9889SGreg Roach     * a form that was generated by the current session.
272a45f9889SGreg Roach     *
273a45f9889SGreg Roach     * @return string
274a45f9889SGreg Roach     */
2758f53f488SRico Sonntag    public static function getCsrfToken(): string
276a45f9889SGreg Roach    {
277a45f9889SGreg Roach        if (!Session::has('CSRF_TOKEN')) {
278*deb4b685SGreg Roach            Session::put('CSRF_TOKEN', Str::random(32));
279a45f9889SGreg Roach        }
280a45f9889SGreg Roach
281a45f9889SGreg Roach        return Session::get('CSRF_TOKEN');
282a45f9889SGreg Roach    }
28331bc7874SGreg Roach}
284