aboutsummaryrefslogtreecommitdiff
path: root/Psso/Session.php
blob: 34dd94ae55847a3b1d08fae5045ad8ad58c031bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
<?php
namespace Psso;

class Session {
    protected static ?self $instance = null;
    protected static ?array $pdoSetup = null;
    protected static ?array $config = null;

    protected \PDO $db;
    private ?string $token = null;

    public static function setDsn(
        array $config,
        string $dsn,
        ?string $username = null,
        #[\SensitiveParameter] ?string $password = null
    ): void {
        static::$config = $config;
        static::$pdoSetup = [$dsn, $username, $password];
    }

    protected function __construct() {
        $args = static::$pdoSetup;
        $args[] = [\PDO::ATTR_PERSISTENT => true];
        $this->db = new \PDO(...$args);
        $this->setup();
    }

    public static function get(): self {
        if (!isset(self::$instance)) {
            self::$instance = new self();
        }
        self::$instance->setCookie();
        return self::$instance;
    }

    protected function setup(): void {
        $this->db->exec(
            <<<'END'
            create table if not exists session (
                token text primary key not null,
                identity text not null default 'N;',
                challenges text not null default 'N;'
            );
            END
        );
    }

    protected function domain(): string {
        return explode('.', $_SERVER['HTTP_HOST'], 2)[1];
    }

    protected function setCookie(): void {
        if (isset($_COOKIE['PSSO_session'])) return;
        header(
            'Set-Cookie: PSSO_session=' . $this->currentToken()
          . '; Domain=' . $this->domain()
          . '; HttpOnly'
          . '; Max-Age=' . static::$config['auth']['lifetime']
          . '; Path=/'
          . '; SameSite=Lax'
          . '; Secure'
        );
    }

    protected function currentToken(): string {
        return $this->token = $this->token ??
                              $_COOKIE['PSSO_session'] ??
                              base64_encode(random_bytes(129));
    }

    protected function setColumn(string $column, mixed $data): void {
        $stmt = $this->db->prepare(
            "insert into session(token, $column) values (?, ?) "
          . "on conflict do update set $column=excluded.$column"
        );
        $stmt->execute([$this->currentToken(), serialize($data)]);
    }

    public function getColumn(string $column): mixed {
        $stmt = $this->db->prepare("select $column from session where token=?");
        $stmt->execute([$this->currentToken()]);
        return unserialize($stmt->fetchColumn(0)) ?: null;
    }

    public function setIdentity(mixed $identity): void {
        $this->setColumn('identity', $identity);
    }

    public function getIdentity(): mixed {
        return $this->getColumn('identity');
    }

    public function setChallenges(mixed $challenges): void {
        $this->setColumn('challenges', $challenges);
    }

    public function getChallenges(): mixed {
        return $this->getColumn('challenges');
    }
}