1<?php
2
3namespace dokuwiki\plugin\oauth;
4
5/**
6 * Singleton to manage all oAuth related session and cookie data
7 */
8class Session
9{
10    /** @var Session */
11    protected static $instance;
12
13    /**
14     * hidden constructor
15     */
16    protected function __construct()
17    {
18    }
19
20    /**
21     * Get Singleton Instance
22     *
23     * @return Session
24     */
25    public static function getInstance()
26    {
27        if (self::$instance === null) {
28            self::$instance = new Session();
29        }
30        return self::$instance;
31    }
32
33    /**
34     * Set the environment needed to verify a login in progress
35     *
36     * @param string $servicename the name of the service used
37     * @param string $id pageID to return to after login
38     * @return void
39     */
40    public function setLoginData($servicename, $id)
41    {
42        $_SESSION[DOKU_COOKIE]['auth']['oauth'] = [
43            'servicename' => $servicename,
44            'id' => $id,
45        ];
46    }
47
48    /**
49     * Get the current login environment
50     *
51     * @return false|array Either [servicename=>*, id=>*] or false
52     */
53    public function getLoginData()
54    {
55        return $_SESSION[DOKU_COOKIE]['auth']['oauth'] ?? false;
56    }
57
58    /**
59     * Clear login environment after login
60     *
61     * @return void
62     */
63    public function clearLoginData()
64    {
65        if (isset($_SESSION[DOKU_COOKIE]['auth']['oauth'])) {
66            unset($_SESSION[DOKU_COOKIE]['auth']['oauth']);
67        }
68    }
69
70    /**
71     * This basically duplicates what DokuWiki does when a user is logged in
72     *
73     * @param array $userdata
74     * @param bool $resettime Set a new session time? False only when restoring from session
75     * @return void
76     * @throws Exception
77     */
78    public function setUser($userdata, $resettime = true)
79    {
80        global $USERINFO;
81
82        if (
83            !isset($userdata['user']) ||
84            !isset($userdata['name']) ||
85            !isset($userdata['mail']) ||
86            !isset($userdata['grps']) ||
87            !is_array($userdata['grps'])
88        ) {
89            throw new Exception('Missing user data, cannot save to session');
90        }
91
92        $USERINFO = $userdata;
93        $_SERVER['REMOTE_USER'] = $userdata['user'];
94
95        $_SESSION[DOKU_COOKIE]['auth']['user'] = $userdata['user'];
96        $_SESSION[DOKU_COOKIE]['auth']['pass'] = 'not-set'; // pass is neither needed nor wanted
97        $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
98        $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid();
99        if ($resettime) {
100            $_SESSION[DOKU_COOKIE]['auth']['time'] = time();
101        }
102    }
103
104    /**
105     * The user data currently saved in the session if any
106     *
107     * @return false|array
108     */
109    public function getUser()
110    {
111        return $_SESSION[DOKU_COOKIE]['auth']['info'] ?? false;
112    }
113
114    /**
115     * Set oAuth info to cookie
116     *
117     * We use the same cookie as standard DokuWiki, but write different info.
118     *
119     * @param string $servicename
120     * @param string $storageId
121     * @return void
122     */
123    public function setCookie($servicename, $storageId)
124    {
125        global $conf;
126        $validityPeriodInSeconds = 60 * 60 * 24 * 365;
127        $cookie = "$servicename|oauth|$storageId";
128        $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
129        $time = time() + $validityPeriodInSeconds;
130        setcookie(
131            DOKU_COOKIE,
132            $cookie,
133            [
134                'expires' => $time,
135                'path' => $cookieDir,
136                'domain' => '',
137                'secure' => $conf['securecookie'] && is_ssl(),
138                'httponly' => true
139            ]
140        );
141    }
142
143    /**
144     * Get oAuth info from cookie
145     *
146     * @return array|false Either [servicename=>?, storageID=>?] or false if no oauth data in cookie
147     */
148    public function getCookie()
149    {
150        if (!isset($_COOKIE[DOKU_COOKIE])) return false;
151        [$servicename, $oauth, $storageId] = explode('|', $_COOKIE[DOKU_COOKIE]);
152        if ($oauth !== 'oauth') return false;
153        return ['servicename' => $servicename, 'storageId' => $storageId];
154    }
155
156    /**
157     * Is any auth data in the session currently trustworthy?
158     * @return bool
159     */
160    public function isValid()
161    {
162        global $conf;
163
164        if (!isset($_SESSION[DOKU_COOKIE]['auth']['buid'])) return false;
165        if (!isset($_SESSION[DOKU_COOKIE]['auth']['time'])) return false;
166        if ($_SESSION[DOKU_COOKIE]['auth']['buid'] != auth_browseruid()) return false;
167        if ($_SESSION[DOKU_COOKIE]['auth']['time'] < time() - $conf['auth_security_timeout']) return false;
168
169        return true;
170    }
171
172    /**
173     * Clear the session from auth related data
174     * @return void
175     */
176    public function clear()
177    {
178        //FIXME clear cookie?
179        $this->clearLoginData();
180    }
181}
182