xref: /plugin/oauth/auth.php (revision 0aa332ffe96d59de83453551ff302435906e621b)
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(($session['time'] >= time() - $conf['auth_security_timeout']) &&
54                ($session['buid'] == auth_browseruid())
55            ) {
56
57                $_SERVER['REMOTE_USER'] = $session['user'];
58                $USERINFO               = $session['info'];
59                return true;
60            }
61        }
62
63        // either we're in oauth login or a previous log needs to be rechecked
64        if(isset($servicename)) {
65            /** @var helper_plugin_oauth $hlp */
66            $hlp     = plugin_load('helper', 'oauth');
67            $service = $hlp->loadService($servicename);
68            if(is_null($service)) return false;
69
70            if($service->checkToken()) {
71
72
73                $uinfo = $service->getUser();
74
75                $uinfo['user'] = $this->cleanUser((string) $uinfo['user']);
76                if(!$uinfo['name']) $uinfo['name'] = $uinfo['user'];
77
78                if(!$uinfo['user'] || !$uinfo['mail']) {
79                    msg("$servicename did not provide the needed user info. Can't log you in", -1);
80                    return false;
81                }
82
83                // see if the user is known already
84                $user = $this->getUserByEmail($uinfo['mail']);
85                if($user) {
86                    $sinfo = $this->getUserData($user);
87                    // check if the user allowed access via this service
88                    if(!in_array($this->cleanGroup($servicename), $sinfo['grps'])) {
89                        msg(sprintf($this->getLang('authnotenabled'), $servicename), -1);
90                        return false;
91                    }
92                    $uinfo['user'] = $user;
93                    $uinfo['name'] = $sinfo['name'];
94                    $uinfo['grps'] = array_merge((array) $uinfo['grps'], $sinfo['grps']);
95                } elseif (strpos($conf['disableactions'],'register') === False) {
96                    // new user, create him - making sure the login is unique by adding a number if needed
97                    $user  = $uinfo['user'];
98                    $count = '';
99                    while($this->getUserData($user . $count)) {
100                        if($count) {
101                            $count++;
102                        } else {
103                            $count = 1;
104                        }
105                    }
106                    $user            = $user . $count;
107                    $uinfo['user']   = $user;
108                    $groups_on_creation = array();
109                    $groups_on_creation[] = $conf['defaultgroup'];
110                    $groups_on_creation[] = $this->cleanGroup($servicename); // add service as group
111                    $uinfo['grps'] = array_merge((array) $uinfo['grps'], $groups_on_creation);
112
113                    $ok = $this->triggerUserMod('create',array($user, auth_pwgen($user), $uinfo['name'], $uinfo['mail'],
114                                                          $groups_on_creation));
115                    if(!$ok) {
116                        msg('something went wrong creating your user account. please try again later.', -1);
117                        return false;
118                    }
119
120                    // send notification about the new user
121                    $subscription = new Subscription();
122                    $subscription->send_register($user, $uinfo['name'], $uinfo['mail']);
123                } else {
124                    msg('Self-Registration is currently disabled. Please ask your DokuWiki administrator to create your account manually.', -1);
125                    return false;
126                }
127
128                // set user session
129                $this->setUserSession($uinfo, $servicename);
130
131                $cookie = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode('oauth').'|'.base64_encode($servicename);
132                $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
133                $time      = $sticky ? (time() + 60 * 60 * 24 * 365) : 0;
134                setcookie(DOKU_COOKIE,$cookie, $time, $cookieDir, '',($conf['securecookie'] && is_ssl()), true);
135
136                if(isset($page)) {
137                    send_redirect(wl($page));
138                }
139                return true;
140            } else {
141                $this->relogin($servicename);
142            }
143
144            unset($_SESSION[DOKU_COOKIE]['auth']);
145            return false; // something went wrong during oAuth login
146        } elseif (isset($_COOKIE[DOKU_COOKIE])) {
147            global $INPUT;
148            //try cookie
149            list($cookieuser, $cookiesticky, $auth, $servicename) = explode('|', $_COOKIE[DOKU_COOKIE]);
150            $cookieuser = base64_decode($cookieuser, true);
151            $auth = base64_decode($auth, true);
152            $servicename = base64_decode($servicename, true);
153            if ($auth === 'oauth') {
154                $this->relogin($servicename);
155            }
156        }
157
158        // do the "normal" plain auth login via form
159        return auth_login($user, $pass, $sticky);
160    }
161
162    protected function relogin($servicename) {
163        global $INPUT;
164
165        /** @var helper_plugin_oauth $hlp */
166        $hlp     = plugin_load('helper', 'oauth');
167        $service     = $hlp->loadService($servicename);
168        if(is_null($service)) return false;
169
170        // remember service in session
171        session_start();
172        $_SESSION[DOKU_COOKIE]['oauth-inprogress']['service'] = $servicename;
173        $_SESSION[DOKU_COOKIE]['oauth-inprogress']['id']      = $INPUT->str('id');
174
175        $str_vars = array('wikitext', 'prefix', 'suffix', 'summary', 'sectok', 'target', 'range', 'rev', 'at');
176        foreach ($str_vars as $input_var) {
177            if ($INPUT->str($input_var) !== '') {
178                $_SESSION[DOKU_COOKIE]['oauth-done'][$input_var] = $INPUT->str($input_var);
179            }
180
181            if ($INPUT->post->str($input_var) !== '') {
182                $_SESSION[DOKU_COOKIE]['oauth-done']['post'][$input_var] = $INPUT->post->str($input_var);
183            }
184
185            if ($INPUT->get->str($input_var) !== '') {
186                $_SESSION[DOKU_COOKIE]['oauth-done']['get'][$input_var] = $INPUT->get->str($input_var);
187            }
188        }
189
190        if (is_array($INPUT->post->param('do'))) {
191            $doPost = key($INPUT->post->arr('do'));
192        } else {
193            $doPost = $INPUT->post->str('do');
194        }
195        $doGet = $INPUT->get->str('do');
196        if (!empty($doPost)) {
197            $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doPost;
198        } elseif (!empty($doGet)) {
199            $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doGet;
200        }
201
202        session_write_close();
203
204        $service->login();
205    }
206
207    /**
208     * @param array  $data
209     * @param string $service
210     */
211    protected function setUserSession($data, $service) {
212        global $USERINFO;
213        global $conf;
214
215        // set up groups
216        if(!is_array($data['grps'])) {
217            $data['grps'] = array();
218        }
219        $data['grps'][] = $this->cleanGroup($service);
220        $data['grps']   = array_unique($data['grps']);
221
222        $USERINFO                               = $data;
223        $_SERVER['REMOTE_USER']                 = $data['user'];
224        $_SESSION[DOKU_COOKIE]['auth']['user']  = $data['user'];
225        $_SESSION[DOKU_COOKIE]['auth']['pass']  = $data['pass'];
226        $_SESSION[DOKU_COOKIE]['auth']['info']  = $USERINFO;
227        $_SESSION[DOKU_COOKIE]['auth']['buid']  = auth_browseruid();
228        $_SESSION[DOKU_COOKIE]['auth']['time']  = time();
229        $_SESSION[DOKU_COOKIE]['auth']['oauth'] = $service;
230    }
231
232    /**
233     * Unset additional stuff in session on logout
234     */
235    public function logOff() {
236        parent::logOff();
237
238        if(isset($_SESSION[DOKU_COOKIE]['auth']['buid'])) {
239            unset($_SESSION[DOKU_COOKIE]['auth']['buid']);
240        }
241        if(isset($_SESSION[DOKU_COOKIE]['auth']['time'])) {
242            unset($_SESSION[DOKU_COOKIE]['auth']['time']);
243        }
244        if(isset($_SESSION[DOKU_COOKIE]['auth']['oauth'])) {
245            unset($_SESSION[DOKU_COOKIE]['auth']['oauth']);
246        }
247    }
248
249    /**
250     * Find a user by his email address
251     *
252     * @param $mail
253     * @return bool|string
254     */
255    protected function getUserByEmail($mail) {
256        if($this->users === null) $this->_loadUserData();
257        $mail = strtolower($mail);
258
259        foreach($this->users as $user => $uinfo) {
260            if(strtolower($uinfo['mail']) == $mail) return $user;
261        }
262
263        return false;
264    }
265
266    /**
267     * Enhance function to check aainst duplicate emails
268     *
269     * @param string $user
270     * @param string $pwd
271     * @param string $name
272     * @param string $mail
273     * @param null   $grps
274     * @return bool|null|string
275     */
276    public function createUser($user, $pwd, $name, $mail, $grps = null) {
277        if($this->getUserByEmail($mail)) {
278            msg($this->getLang('emailduplicate'), -1);
279            return false;
280        }
281
282        return parent::createUser($user, $pwd, $name, $mail, $grps);
283    }
284
285    /**
286     * Enhance function to check aainst duplicate emails
287     *
288     * @param string $user
289     * @param array  $changes
290     * @return bool
291     */
292    public function modifyUser($user, $changes) {
293        global $conf;
294
295        if(isset($changes['mail'])) {
296            $found = $this->getUserByEmail($changes['mail']);
297            if($found != $user) {
298                msg($this->getLang('emailduplicate'), -1);
299                return false;
300            }
301        }
302
303        $ok = parent::modifyUser($user, $changes);
304
305        // refresh session cache
306        touch($conf['cachedir'] . '/sessionpurge');
307
308        return $ok;
309    }
310
311}
312
313// vim:ts=4:sw=4:et:
314