xref: /plugin/oauth/auth.php (revision f07c7607ac259a01f28d12187a97149597047675)
1<?php
2/**
3 * DokuWiki Plugin oauth (Auth Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Andreas Gohr <andi@splitbrain.org>
7 */
8
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12class auth_plugin_oauth extends auth_plugin_authplain {
13
14    /**
15     * Constructor
16     *
17     * Sets capabilities.
18     */
19    public function __construct() {
20        parent::__construct();
21
22        $this->cando['external'] = true;
23    }
24
25    /**
26     * Handle the login
27     *
28     * This either trusts the session data (if any), processes the second oAuth step or simply
29     * executes a normal plugin against local users.
30     *
31     * @param string $user
32     * @param string $pass
33     * @param bool   $sticky
34     * @return bool
35     */
36    function trustExternal($user, $pass, $sticky = false) {
37        global $conf;
38        global $USERINFO;
39
40        // are we in login progress?
41        if(isset($_SESSION[DOKU_COOKIE]['oauth-inprogress'])) {
42            $servicename = $_SESSION[DOKU_COOKIE]['oauth-inprogress']['service'];
43            $page        = $_SESSION[DOKU_COOKIE]['oauth-inprogress']['id'];
44
45            unset($_SESSION[DOKU_COOKIE]['oauth-inprogress']);
46        }
47
48        // check session for existing oAuth login data
49        $session = $_SESSION[DOKU_COOKIE]['auth'];
50        if(!isset($servicename) && isset($session['oauth'])) {
51            $servicename = $session['oauth'];
52            // check if session data is still considered valid
53            if ($this->isSessionValid($session)) {
54                $_SERVER['REMOTE_USER'] = $session['user'];
55                $USERINFO               = $session['info'];
56                return true;
57            }
58        }
59
60        // either we're in oauth login or a previous log needs to be rechecked
61        if(isset($servicename)) {
62            /** @var helper_plugin_oauth $hlp */
63            $hlp     = plugin_load('helper', 'oauth');
64            $service = $hlp->loadService($servicename);
65            if(is_null($service)) return false;
66
67            if($service->checkToken()) {
68
69
70                $uinfo = $service->getUser();
71
72                $uinfo['user'] = $this->cleanUser((string) $uinfo['user']);
73                if(!$uinfo['name']) $uinfo['name'] = $uinfo['user'];
74
75                if(!$uinfo['user'] || !$uinfo['mail']) {
76                    msg("$servicename did not provide the needed user info. Can't log you in", -1);
77                    return false;
78                }
79
80                // see if the user is known already
81                $user = $this->getUserByEmail($uinfo['mail']);
82                if($user) {
83                    $sinfo = $this->getUserData($user);
84                    // check if the user allowed access via this service
85                    if(!in_array($this->cleanGroup($servicename), $sinfo['grps'])) {
86                        msg(sprintf($this->getLang('authnotenabled'), $servicename), -1);
87                        return false;
88                    }
89                    $uinfo['user'] = $user;
90                    $uinfo['name'] = $sinfo['name'];
91                    $uinfo['grps'] = array_merge((array) $uinfo['grps'], $sinfo['grps']);
92                } elseif (actionOK('register')) {
93                    $ok = $this->addUser($uinfo, $servicename);
94                    if (!$ok) {
95                        msg('something went wrong creating your user account. please try again later.', -1);
96                        return false;
97                    }
98                } else {
99                    msg($this->getLang('addUser not possible'), -1);
100                    return false;
101                }
102
103                // set user session
104                $this->setUserSession($uinfo, $servicename);
105
106                $cookie = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode('oauth').'|'.base64_encode($servicename);
107                $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
108                $time      = $sticky ? (time() + 60 * 60 * 24 * 365) : 0;
109                setcookie(DOKU_COOKIE,$cookie, $time, $cookieDir, '',($conf['securecookie'] && is_ssl()), true);
110
111                if(isset($page)) {
112                    send_redirect(wl($page));
113                }
114                return true;
115            } else {
116                $this->relogin($servicename);
117            }
118
119            unset($_SESSION[DOKU_COOKIE]['auth']);
120            return false; // something went wrong during oAuth login
121        } elseif (isset($_COOKIE[DOKU_COOKIE])) {
122            global $INPUT;
123            //try cookie
124            list($cookieuser, $cookiesticky, $auth, $servicename) = explode('|', $_COOKIE[DOKU_COOKIE]);
125            $cookieuser = base64_decode($cookieuser, true);
126            $auth = base64_decode($auth, true);
127            $servicename = base64_decode($servicename, true);
128            if ($auth === 'oauth') {
129                $this->relogin($servicename);
130            }
131        }
132
133        // do the "normal" plain auth login via form
134        return auth_login($user, $pass, $sticky);
135    }
136
137    /**
138     * @param array $session cookie auth session
139     *
140     * @return bool
141     */
142    protected function isSessionValid ($session) {
143        /** @var helper_plugin_oauth $hlp */
144        $hlp     = plugin_load('helper', 'oauth');
145        if ($hlp->validBrowserID($session)) {
146            if (!$hlp->isSessionTimedOut($session)) {
147                return true;
148            } elseif (!($hlp->isGETRequest() && $hlp->isDokuPHP())) {
149                // only force a recheck on a timed-out session during a GET request on the main script doku.php
150                return true;
151            }
152        }
153        return false;
154    }
155
156    protected function relogin($servicename) {
157        global $INPUT;
158
159        /** @var helper_plugin_oauth $hlp */
160        $hlp     = plugin_load('helper', 'oauth');
161        $service     = $hlp->loadService($servicename);
162        if(is_null($service)) return false;
163
164        // remember service in session
165        session_start();
166        $_SESSION[DOKU_COOKIE]['oauth-inprogress']['service'] = $servicename;
167        $_SESSION[DOKU_COOKIE]['oauth-inprogress']['id']      = $INPUT->str('id');
168
169        $_SESSION[DOKU_COOKIE]['oauth-done']['$_REQUEST'] = $_REQUEST;
170
171        if (is_array($INPUT->post->param('do'))) {
172            $doPost = key($INPUT->post->arr('do'));
173        } else {
174            $doPost = $INPUT->post->str('do');
175        }
176        $doGet = $INPUT->get->str('do');
177        if (!empty($doPost)) {
178            $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doPost;
179        } elseif (!empty($doGet)) {
180            $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doGet;
181        }
182
183        session_write_close();
184
185        $service->login();
186    }
187
188    /**
189     * @param array  $data
190     * @param string $service
191     */
192    protected function setUserSession($data, $service) {
193        global $USERINFO;
194        global $conf;
195
196        // set up groups
197        if(!is_array($data['grps'])) {
198            $data['grps'] = array();
199        }
200        $data['grps'][] = $this->cleanGroup($service);
201        $data['grps']   = array_unique($data['grps']);
202
203        $USERINFO                               = $data;
204        $_SERVER['REMOTE_USER']                 = $data['user'];
205        $_SESSION[DOKU_COOKIE]['auth']['user']  = $data['user'];
206        $_SESSION[DOKU_COOKIE]['auth']['pass']  = $data['pass'];
207        $_SESSION[DOKU_COOKIE]['auth']['info']  = $USERINFO;
208        $_SESSION[DOKU_COOKIE]['auth']['buid']  = auth_browseruid();
209        $_SESSION[DOKU_COOKIE]['auth']['time']  = time();
210        $_SESSION[DOKU_COOKIE]['auth']['oauth'] = $service;
211    }
212
213    /**
214     * Unset additional stuff in session on logout
215     */
216    public function logOff() {
217        parent::logOff();
218
219        if(isset($_SESSION[DOKU_COOKIE]['auth']['buid'])) {
220            unset($_SESSION[DOKU_COOKIE]['auth']['buid']);
221        }
222        if(isset($_SESSION[DOKU_COOKIE]['auth']['time'])) {
223            unset($_SESSION[DOKU_COOKIE]['auth']['time']);
224        }
225        if(isset($_SESSION[DOKU_COOKIE]['auth']['oauth'])) {
226            unset($_SESSION[DOKU_COOKIE]['auth']['oauth']);
227        }
228    }
229
230    /**
231     * Find a user by his email address
232     *
233     * @param $mail
234     * @return bool|string
235     */
236    protected function getUserByEmail($mail) {
237        if($this->users === null) $this->_loadUserData();
238        $mail = strtolower($mail);
239
240        foreach($this->users as $user => $uinfo) {
241            if(strtolower($uinfo['mail']) == $mail) return $user;
242        }
243
244        return false;
245    }
246
247    /**
248     * Enhance function to check against duplicate emails
249     *
250     * @param string $user
251     * @param string $pwd
252     * @param string $name
253     * @param string $mail
254     * @param null   $grps
255     * @return bool|null|string
256     */
257    public function createUser($user, $pwd, $name, $mail, $grps = null) {
258        if($this->getUserByEmail($mail)) {
259            msg($this->getLang('emailduplicate'), -1);
260            return false;
261        }
262
263        return parent::createUser($user, $pwd, $name, $mail, $grps);
264    }
265
266    /**
267     * Enhance function to check aainst duplicate emails
268     *
269     * @param string $user
270     * @param array  $changes
271     * @return bool
272     */
273    public function modifyUser($user, $changes) {
274        global $conf;
275
276        if(isset($changes['mail'])) {
277            $found = $this->getUserByEmail($changes['mail']);
278            if($found != $user) {
279                msg($this->getLang('emailduplicate'), -1);
280                return false;
281            }
282        }
283
284        $ok = parent::modifyUser($user, $changes);
285
286        // refresh session cache
287        touch($conf['cachedir'] . '/sessionpurge');
288
289        return $ok;
290    }
291
292    /**
293     * new user, create him - making sure the login is unique by adding a number if needed
294     *
295     * @param $uinfo
296     * @param $servicename
297     *
298     * @return bool
299     */
300    protected function addUser($uinfo, $servicename) {
301        global $conf;
302        $user = $uinfo['user'];
303        $count = '';
304        while($this->getUserData($user . $count)) {
305            if($count) {
306                $count++;
307            } else {
308                $count = 1;
309            }
310        }
311        $user = $user . $count;
312        $uinfo['user'] = $user;
313        $groups_on_creation = array();
314        $groups_on_creation[] = $conf['defaultgroup'];
315        $groups_on_creation[] = $this->cleanGroup($servicename); // add service as group
316        $uinfo['grps'] = array_merge((array) $uinfo['grps'], $groups_on_creation);
317
318        $ok = $this->triggerUserMod(
319            'create',
320            array($user, auth_pwgen($user), $uinfo['name'], $uinfo['mail'], $groups_on_creation,)
321        );
322        if(!$ok) {
323            return false;
324        }
325
326        // send notification about the new user
327        $subscription = new Subscription();
328        $subscription->send_register($user, $uinfo['name'], $uinfo['mail']);
329        return true;
330    }
331
332}
333
334// vim:ts=4:sw=4:et:
335