xref: /plugin/oauth/auth.php (revision f2e164b0a85700a431df521494ba9906d2482c87)
180852c15SAndreas Gohr<?php
280852c15SAndreas Gohr/**
380852c15SAndreas Gohr * DokuWiki Plugin oauth (Auth Component)
480852c15SAndreas Gohr *
580852c15SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
680852c15SAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
780852c15SAndreas Gohr */
880852c15SAndreas Gohr
980852c15SAndreas Gohr// must be run within Dokuwiki
1080852c15SAndreas Gohrif(!defined('DOKU_INC')) die();
1180852c15SAndreas Gohr
12f10e09e2SAndreas Gohrclass auth_plugin_oauth extends auth_plugin_authplain {
1380852c15SAndreas Gohr
14f866280eSAndreas Gohr    /**
15f866280eSAndreas Gohr     * Constructor
16f866280eSAndreas Gohr     *
17f866280eSAndreas Gohr     * Sets capabilities.
18f866280eSAndreas Gohr     */
1980852c15SAndreas Gohr    public function __construct() {
20f10e09e2SAndreas Gohr        parent::__construct();
2180852c15SAndreas Gohr
22f10e09e2SAndreas Gohr        $this->cando['external'] = true;
2380852c15SAndreas Gohr    }
2480852c15SAndreas Gohr
25f866280eSAndreas Gohr    /**
26f866280eSAndreas Gohr     * Handle the login
27f866280eSAndreas Gohr     *
28f866280eSAndreas Gohr     * This either trusts the session data (if any), processes the second oAuth step or simply
29f866280eSAndreas Gohr     * executes a normal plugin against local users.
30f866280eSAndreas Gohr     *
31f866280eSAndreas Gohr     * @param string $user
32f866280eSAndreas Gohr     * @param string $pass
33f866280eSAndreas Gohr     * @param bool   $sticky
34f866280eSAndreas Gohr     * @return bool
35f866280eSAndreas Gohr     */
36f10e09e2SAndreas Gohr    function trustExternal($user, $pass, $sticky = false) {
37a7a8f46aSAndreas Gohr        global $conf;
38a7a8f46aSAndreas Gohr        global $USERINFO;
3980852c15SAndreas Gohr
402e94f0b8SAndreas Gohr        // are we in login progress?
412e94f0b8SAndreas Gohr        if(isset($_SESSION[DOKU_COOKIE]['oauth-inprogress'])) {
422e94f0b8SAndreas Gohr            $servicename = $_SESSION[DOKU_COOKIE]['oauth-inprogress']['service'];
432e94f0b8SAndreas Gohr            $page        = $_SESSION[DOKU_COOKIE]['oauth-inprogress']['id'];
442e94f0b8SAndreas Gohr
452e94f0b8SAndreas Gohr            unset($_SESSION[DOKU_COOKIE]['oauth-inprogress']);
462e94f0b8SAndreas Gohr        }
4780852c15SAndreas Gohr
48a7a8f46aSAndreas Gohr        // check session for existing oAuth login data
49a7a8f46aSAndreas Gohr        $session = $_SESSION[DOKU_COOKIE]['auth'];
502e94f0b8SAndreas Gohr        if(!isset($servicename) && isset($session['oauth'])) {
51a7a8f46aSAndreas Gohr            $servicename = $session['oauth'];
52a7a8f46aSAndreas Gohr            // check if session data is still considered valid
53*f2e164b0SMichael Große            if ($this->isSessionValid($session)) {
54a7a8f46aSAndreas Gohr                $_SERVER['REMOTE_USER'] = $session['user'];
55a7a8f46aSAndreas Gohr                $USERINFO               = $session['info'];
5680852c15SAndreas Gohr                return true;
57f10e09e2SAndreas Gohr            }
5880852c15SAndreas Gohr        }
5980852c15SAndreas Gohr
60a7a8f46aSAndreas Gohr        // either we're in oauth login or a previous log needs to be rechecked
612e94f0b8SAndreas Gohr        if(isset($servicename)) {
62a7a8f46aSAndreas Gohr            /** @var helper_plugin_oauth $hlp */
63a7a8f46aSAndreas Gohr            $hlp     = plugin_load('helper', 'oauth');
64a7a8f46aSAndreas Gohr            $service = $hlp->loadService($servicename);
65a7a8f46aSAndreas Gohr            if(is_null($service)) return false;
66a7a8f46aSAndreas Gohr
67a7a8f46aSAndreas Gohr            if($service->checkToken()) {
68213f4618SMichael Große
69213f4618SMichael Große
70a7a8f46aSAndreas Gohr                $uinfo = $service->getUser();
71f866280eSAndreas Gohr
721025aad7SAndreas Gohr                $uinfo['user'] = $this->cleanUser((string) $uinfo['user']);
731025aad7SAndreas Gohr                if(!$uinfo['name']) $uinfo['name'] = $uinfo['user'];
741025aad7SAndreas Gohr
751025aad7SAndreas Gohr                if(!$uinfo['user'] || !$uinfo['mail']) {
761025aad7SAndreas Gohr                    msg("$servicename did not provide the needed user info. Can't log you in", -1);
771025aad7SAndreas Gohr                    return false;
781025aad7SAndreas Gohr                }
791025aad7SAndreas Gohr
80f866280eSAndreas Gohr                // see if the user is known already
81f866280eSAndreas Gohr                $user = $this->getUserByEmail($uinfo['mail']);
82f866280eSAndreas Gohr                if($user) {
83f866280eSAndreas Gohr                    $sinfo = $this->getUserData($user);
843c0138dbSAndreas Gohr                    // check if the user allowed access via this service
853c0138dbSAndreas Gohr                    if(!in_array($this->cleanGroup($servicename), $sinfo['grps'])) {
863c0138dbSAndreas Gohr                        msg(sprintf($this->getLang('authnotenabled'), $servicename), -1);
873c0138dbSAndreas Gohr                        return false;
883c0138dbSAndreas Gohr                    }
89f866280eSAndreas Gohr                    $uinfo['user'] = $user;
90f866280eSAndreas Gohr                    $uinfo['name'] = $sinfo['name'];
91f866280eSAndreas Gohr                    $uinfo['grps'] = array_merge((array) $uinfo['grps'], $sinfo['grps']);
92ebf681d1SMichael Große                } elseif (actionOK('register')) {
93f866280eSAndreas Gohr                    // new user, create him - making sure the login is unique by adding a number if needed
94f866280eSAndreas Gohr                    $user  = $uinfo['user'];
95f866280eSAndreas Gohr                    $count = '';
96f866280eSAndreas Gohr                    while($this->getUserData($user . $count)) {
97f866280eSAndreas Gohr                        if($count) {
98f866280eSAndreas Gohr                            $count++;
99f866280eSAndreas Gohr                        } else {
100f866280eSAndreas Gohr                            $count = 1;
101f866280eSAndreas Gohr                        }
102f866280eSAndreas Gohr                    }
103f866280eSAndreas Gohr                    $user            = $user . $count;
104f866280eSAndreas Gohr                    $uinfo['user']   = $user;
1056047eb11SMichael Große                    $groups_on_creation = array();
1066047eb11SMichael Große                    $groups_on_creation[] = $conf['defaultgroup'];
1076047eb11SMichael Große                    $groups_on_creation[] = $this->cleanGroup($servicename); // add service as group
1086047eb11SMichael Große                    $uinfo['grps'] = array_merge((array) $uinfo['grps'], $groups_on_creation);
109f866280eSAndreas Gohr
1106c23164dSMichael Große                    $ok = $this->triggerUserMod('create',array($user, auth_pwgen($user), $uinfo['name'], $uinfo['mail'],
1116c23164dSMichael Große                                                          $groups_on_creation));
112caa5ded4SAndreas Gohr                    if(!$ok) {
113caa5ded4SAndreas Gohr                        msg('something went wrong creating your user account. please try again later.', -1);
114caa5ded4SAndreas Gohr                        return false;
115caa5ded4SAndreas Gohr                    }
116caa5ded4SAndreas Gohr
117caa5ded4SAndreas Gohr                    // send notification about the new user
118caa5ded4SAndreas Gohr                    $subscription = new Subscription();
119caa5ded4SAndreas Gohr                    $subscription->send_register($user, $uinfo['name'], $uinfo['mail']);
1200aa332ffSMichael Große                } else {
1210aa332ffSMichael Große                    msg('Self-Registration is currently disabled. Please ask your DokuWiki administrator to create your account manually.', -1);
1220aa332ffSMichael Große                    return false;
123f866280eSAndreas Gohr                }
124f866280eSAndreas Gohr
125f866280eSAndreas Gohr                // set user session
126a7a8f46aSAndreas Gohr                $this->setUserSession($uinfo, $servicename);
127213f4618SMichael Große
128213f4618SMichael Große                $cookie = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode('oauth').'|'.base64_encode($servicename);
129213f4618SMichael Große                $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
130213f4618SMichael Große                $time      = $sticky ? (time() + 60 * 60 * 24 * 365) : 0;
131213f4618SMichael Große                setcookie(DOKU_COOKIE,$cookie, $time, $cookieDir, '',($conf['securecookie'] && is_ssl()), true);
132213f4618SMichael Große
1334485a349SMichael Große                if(isset($page)) {
1344485a349SMichael Große                    send_redirect(wl($page));
1354485a349SMichael Große                }
136a7a8f46aSAndreas Gohr                return true;
137213f4618SMichael Große            } else {
138213f4618SMichael Große                $this->relogin($servicename);
139a7a8f46aSAndreas Gohr            }
140a7a8f46aSAndreas Gohr
141936b9c9cSMichael Große            unset($_SESSION[DOKU_COOKIE]['auth']);
142a7a8f46aSAndreas Gohr            return false; // something went wrong during oAuth login
143213f4618SMichael Große        } elseif (isset($_COOKIE[DOKU_COOKIE])) {
144213f4618SMichael Große            global $INPUT;
145213f4618SMichael Große            //try cookie
146213f4618SMichael Große            list($cookieuser, $cookiesticky, $auth, $servicename) = explode('|', $_COOKIE[DOKU_COOKIE]);
147213f4618SMichael Große            $cookieuser = base64_decode($cookieuser, true);
148213f4618SMichael Große            $auth = base64_decode($auth, true);
149213f4618SMichael Große            $servicename = base64_decode($servicename, true);
150213f4618SMichael Große            if ($auth === 'oauth') {
151213f4618SMichael Große                $this->relogin($servicename);
152213f4618SMichael Große            }
15380852c15SAndreas Gohr        }
15480852c15SAndreas Gohr
155a7a8f46aSAndreas Gohr        // do the "normal" plain auth login via form
156a7a8f46aSAndreas Gohr        return auth_login($user, $pass, $sticky);
157a7a8f46aSAndreas Gohr    }
15880852c15SAndreas Gohr
159*f2e164b0SMichael Große    /**
160*f2e164b0SMichael Große     * @param array $session cookie auth session
161*f2e164b0SMichael Große     *
162*f2e164b0SMichael Große     * @return bool
163*f2e164b0SMichael Große     */
164*f2e164b0SMichael Große    protected function isSessionValid ($session) {
165*f2e164b0SMichael Große        /** @var helper_plugin_oauth $hlp */
166*f2e164b0SMichael Große        $hlp     = plugin_load('helper', 'oauth');
167*f2e164b0SMichael Große        if ($hlp->validBrowserID($session)) {
168*f2e164b0SMichael Große            if (!$hlp->isSessionTimedOut($session)) {
169*f2e164b0SMichael Große                return true;
170*f2e164b0SMichael Große            } elseif (!($hlp->isGETRequest() && $hlp->isDokuPHP())) {
171*f2e164b0SMichael Große                // only force a recheck on a timed-out session during a GET request on the main script doku.php
172*f2e164b0SMichael Große                return true;
173*f2e164b0SMichael Große            }
174*f2e164b0SMichael Große        }
175*f2e164b0SMichael Große        return false;
176*f2e164b0SMichael Große    }
177*f2e164b0SMichael Große
178213f4618SMichael Große    protected function relogin($servicename) {
179213f4618SMichael Große        global $INPUT;
180213f4618SMichael Große
181213f4618SMichael Große        /** @var helper_plugin_oauth $hlp */
182213f4618SMichael Große        $hlp     = plugin_load('helper', 'oauth');
183213f4618SMichael Große        $service     = $hlp->loadService($servicename);
184213f4618SMichael Große        if(is_null($service)) return false;
185213f4618SMichael Große
186213f4618SMichael Große        // remember service in session
187213f4618SMichael Große        session_start();
188213f4618SMichael Große        $_SESSION[DOKU_COOKIE]['oauth-inprogress']['service'] = $servicename;
189213f4618SMichael Große        $_SESSION[DOKU_COOKIE]['oauth-inprogress']['id']      = $INPUT->str('id');
190213f4618SMichael Große
191213f4618SMichael Große        $str_vars = array('wikitext', 'prefix', 'suffix', 'summary', 'sectok', 'target', 'range', 'rev', 'at');
192213f4618SMichael Große        foreach ($str_vars as $input_var) {
193213f4618SMichael Große            if ($INPUT->str($input_var) !== '') {
194213f4618SMichael Große                $_SESSION[DOKU_COOKIE]['oauth-done'][$input_var] = $INPUT->str($input_var);
195213f4618SMichael Große            }
196213f4618SMichael Große
197213f4618SMichael Große            if ($INPUT->post->str($input_var) !== '') {
198213f4618SMichael Große                $_SESSION[DOKU_COOKIE]['oauth-done']['post'][$input_var] = $INPUT->post->str($input_var);
199213f4618SMichael Große            }
200213f4618SMichael Große
201213f4618SMichael Große            if ($INPUT->get->str($input_var) !== '') {
202213f4618SMichael Große                $_SESSION[DOKU_COOKIE]['oauth-done']['get'][$input_var] = $INPUT->get->str($input_var);
203213f4618SMichael Große            }
204213f4618SMichael Große        }
205213f4618SMichael Große
206213f4618SMichael Große        if (is_array($INPUT->post->param('do'))) {
207213f4618SMichael Große            $doPost = key($INPUT->post->arr('do'));
208213f4618SMichael Große        } else {
209213f4618SMichael Große            $doPost = $INPUT->post->str('do');
210213f4618SMichael Große        }
211213f4618SMichael Große        $doGet = $INPUT->get->str('do');
212213f4618SMichael Große        if (!empty($doPost)) {
213213f4618SMichael Große            $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doPost;
214213f4618SMichael Große        } elseif (!empty($doGet)) {
215213f4618SMichael Große            $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doGet;
216213f4618SMichael Große        }
217213f4618SMichael Große
218213f4618SMichael Große        session_write_close();
219213f4618SMichael Große
220213f4618SMichael Große        $service->login();
221213f4618SMichael Große    }
222213f4618SMichael Große
223a7a8f46aSAndreas Gohr    /**
224a7a8f46aSAndreas Gohr     * @param array  $data
225a7a8f46aSAndreas Gohr     * @param string $service
226a7a8f46aSAndreas Gohr     */
227a7a8f46aSAndreas Gohr    protected function setUserSession($data, $service) {
228a7a8f46aSAndreas Gohr        global $USERINFO;
229a7a8f46aSAndreas Gohr        global $conf;
230a7a8f46aSAndreas Gohr
231a7a8f46aSAndreas Gohr        // set up groups
232a7a8f46aSAndreas Gohr        if(!is_array($data['grps'])) {
233a7a8f46aSAndreas Gohr            $data['grps'] = array();
234a7a8f46aSAndreas Gohr        }
235a7a8f46aSAndreas Gohr        $data['grps'][] = $this->cleanGroup($service);
236f866280eSAndreas Gohr        $data['grps']   = array_unique($data['grps']);
23780852c15SAndreas Gohr
238f10e09e2SAndreas Gohr        $USERINFO                               = $data;
239f10e09e2SAndreas Gohr        $_SERVER['REMOTE_USER']                 = $data['user'];
240f10e09e2SAndreas Gohr        $_SESSION[DOKU_COOKIE]['auth']['user']  = $data['user'];
241f10e09e2SAndreas Gohr        $_SESSION[DOKU_COOKIE]['auth']['pass']  = $data['pass'];
242f10e09e2SAndreas Gohr        $_SESSION[DOKU_COOKIE]['auth']['info']  = $USERINFO;
243a7a8f46aSAndreas Gohr        $_SESSION[DOKU_COOKIE]['auth']['buid']  = auth_browseruid();
244a7a8f46aSAndreas Gohr        $_SESSION[DOKU_COOKIE]['auth']['time']  = time();
245a7a8f46aSAndreas Gohr        $_SESSION[DOKU_COOKIE]['auth']['oauth'] = $service;
24680852c15SAndreas Gohr    }
24780852c15SAndreas Gohr
248f866280eSAndreas Gohr    /**
249e32c3607SAndreas Gohr     * Unset additional stuff in session on logout
250e32c3607SAndreas Gohr     */
251e32c3607SAndreas Gohr    public function logOff() {
252e32c3607SAndreas Gohr        parent::logOff();
253e32c3607SAndreas Gohr
254e32c3607SAndreas Gohr        if(isset($_SESSION[DOKU_COOKIE]['auth']['buid'])) {
255e32c3607SAndreas Gohr            unset($_SESSION[DOKU_COOKIE]['auth']['buid']);
256e32c3607SAndreas Gohr        }
257e32c3607SAndreas Gohr        if(isset($_SESSION[DOKU_COOKIE]['auth']['time'])) {
258e32c3607SAndreas Gohr            unset($_SESSION[DOKU_COOKIE]['auth']['time']);
259e32c3607SAndreas Gohr        }
260e32c3607SAndreas Gohr        if(isset($_SESSION[DOKU_COOKIE]['auth']['oauth'])) {
261e32c3607SAndreas Gohr            unset($_SESSION[DOKU_COOKIE]['auth']['oauth']);
262e32c3607SAndreas Gohr        }
263e32c3607SAndreas Gohr    }
264e32c3607SAndreas Gohr
265e32c3607SAndreas Gohr    /**
266f866280eSAndreas Gohr     * Find a user by his email address
267f866280eSAndreas Gohr     *
268f866280eSAndreas Gohr     * @param $mail
269f866280eSAndreas Gohr     * @return bool|string
270f866280eSAndreas Gohr     */
27138378fbbSAndreas Gohr    protected function getUserByEmail($mail) {
272f866280eSAndreas Gohr        if($this->users === null) $this->_loadUserData();
27338378fbbSAndreas Gohr        $mail = strtolower($mail);
274f866280eSAndreas Gohr
275f866280eSAndreas Gohr        foreach($this->users as $user => $uinfo) {
276f866280eSAndreas Gohr            if(strtolower($uinfo['mail']) == $mail) return $user;
27738378fbbSAndreas Gohr        }
27838378fbbSAndreas Gohr
279f866280eSAndreas Gohr        return false;
280f866280eSAndreas Gohr    }
28138378fbbSAndreas Gohr
282f866280eSAndreas Gohr    /**
283f866280eSAndreas Gohr     * Enhance function to check aainst duplicate emails
284f866280eSAndreas Gohr     *
285f866280eSAndreas Gohr     * @param string $user
286f866280eSAndreas Gohr     * @param string $pwd
287f866280eSAndreas Gohr     * @param string $name
288f866280eSAndreas Gohr     * @param string $mail
289f866280eSAndreas Gohr     * @param null   $grps
290f866280eSAndreas Gohr     * @return bool|null|string
291f866280eSAndreas Gohr     */
292f866280eSAndreas Gohr    public function createUser($user, $pwd, $name, $mail, $grps = null) {
293f866280eSAndreas Gohr        if($this->getUserByEmail($mail)) {
294f866280eSAndreas Gohr            msg($this->getLang('emailduplicate'), -1);
295f866280eSAndreas Gohr            return false;
296f866280eSAndreas Gohr        }
297f866280eSAndreas Gohr
29838378fbbSAndreas Gohr        return parent::createUser($user, $pwd, $name, $mail, $grps);
29938378fbbSAndreas Gohr    }
30038378fbbSAndreas Gohr
301f866280eSAndreas Gohr    /**
302f866280eSAndreas Gohr     * Enhance function to check aainst duplicate emails
303f866280eSAndreas Gohr     *
304f866280eSAndreas Gohr     * @param string $user
305f866280eSAndreas Gohr     * @param array  $changes
306f866280eSAndreas Gohr     * @return bool
307f866280eSAndreas Gohr     */
30838378fbbSAndreas Gohr    public function modifyUser($user, $changes) {
3093c0138dbSAndreas Gohr        global $conf;
3103c0138dbSAndreas Gohr
3113c0138dbSAndreas Gohr        if(isset($changes['mail'])) {
3123c0138dbSAndreas Gohr            $found = $this->getUserByEmail($changes['mail']);
3133c0138dbSAndreas Gohr            if($found != $user) {
314f866280eSAndreas Gohr                msg($this->getLang('emailduplicate'), -1);
315f866280eSAndreas Gohr                return false;
316f866280eSAndreas Gohr            }
3173c0138dbSAndreas Gohr        }
31838378fbbSAndreas Gohr
3193c0138dbSAndreas Gohr        $ok = parent::modifyUser($user, $changes);
3203c0138dbSAndreas Gohr
3213c0138dbSAndreas Gohr        // refresh session cache
3223c0138dbSAndreas Gohr        touch($conf['cachedir'] . '/sessionpurge');
3233c0138dbSAndreas Gohr
3243c0138dbSAndreas Gohr        return $ok;
32538378fbbSAndreas Gohr    }
32638378fbbSAndreas Gohr
32780852c15SAndreas Gohr}
32880852c15SAndreas Gohr
32980852c15SAndreas Gohr// vim:ts=4:sw=4:et:
330