xref: /plugin/oauth/auth.php (revision caa5ded4ab08b0e357614b09720b307b0a5a4236)
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 $INPUT;
38        global $conf;
39        global $USERINFO;
40
41        $servicename = $INPUT->str('oa');
42
43        // check session for existing oAuth login data
44        $session = $_SESSION[DOKU_COOKIE]['auth'];
45        if(!$servicename && isset($session['oauth'])) {
46            $servicename = $session['oauth'];
47            // check if session data is still considered valid
48            if(($session['time'] >= time() - $conf['auth_security_timeout']) &&
49                ($session['buid'] == auth_browseruid())
50            ) {
51
52                $_SERVER['REMOTE_USER'] = $session['user'];
53                $USERINFO               = $session['info'];
54                return true;
55            }
56        }
57
58        // either we're in oauth login or a previous log needs to be rechecked
59        if($servicename) {
60            /** @var helper_plugin_oauth $hlp */
61            $hlp     = plugin_load('helper', 'oauth');
62            $service = $hlp->loadService($servicename);
63            if(is_null($service)) return false;
64
65            // get the token
66            if($service->checkToken()) {
67                $uinfo = $service->getUser();
68
69                $uinfo['user'] = $this->cleanUser((string) $uinfo['user']);
70                if(!$uinfo['name']) $uinfo['name'] = $uinfo['user'];
71
72                if(!$uinfo['user'] || !$uinfo['mail']) {
73                    msg("$servicename did not provide the needed user info. Can't log you in", -1);
74                    return false;
75                }
76
77                // see if the user is known already
78                $user = $this->getUserByEmail($uinfo['mail']);
79                if($user) {
80                    $sinfo = $this->getUserData($user);
81                    // check if the user allowed access via this service
82                    if(!in_array($this->cleanGroup($servicename), $sinfo['grps'])) {
83                        msg(sprintf($this->getLang('authnotenabled'), $servicename), -1);
84                        return false;
85                    }
86                    $uinfo['user'] = $user;
87                    $uinfo['name'] = $sinfo['name'];
88                    $uinfo['grps'] = array_merge((array) $uinfo['grps'], $sinfo['grps']);
89                } else {
90                    // new user, create him - making sure the login is unique by adding a number if needed
91                    $user  = $uinfo['user'];
92                    $count = '';
93                    while($this->getUserData($user.$count)) {
94                        if($count) {
95                            $count++;
96                        } else {
97                            $count = 1;
98                        }
99                    }
100                    $user            = $user.$count;
101                    $uinfo['user']   = $user;
102                    $uinfo['grps']   = (array) $uinfo['grps'];
103                    $uinfo['grps'][] = $conf['defaultgroup'];
104                    $uinfo['grps'][] = $this->cleanGroup($servicename); // add service as group
105
106                    //FIXME we should call trigger_user_mod?
107                    $ok = $this->createUser($user, auth_pwgen($user), $uinfo['name'], $uinfo['mail'], $uinfo['grps']);
108                    if(!$ok) {
109                        msg('something went wrong creating your user account. please try again later.', -1);
110                        return false;
111                    }
112
113                    // send notification about the new user
114                    $subscription = new Subscription();
115                    $subscription->send_register($user, $uinfo['name'], $uinfo['mail']);
116                }
117
118                // set user session
119                $this->setUserSession($uinfo, $servicename);
120                return true;
121            }
122
123            return false; // something went wrong during oAuth login
124        }
125
126        // do the "normal" plain auth login via form
127        return auth_login($user, $pass, $sticky);
128    }
129
130    /**
131     * @param array  $data
132     * @param string $service
133     */
134    protected function setUserSession($data, $service) {
135        global $USERINFO;
136        global $conf;
137
138        // set up groups
139        if(!is_array($data['grps'])) {
140            $data['grps'] = array();
141        }
142        $data['grps'][] = $this->cleanGroup($service);
143        $data['grps']   = array_unique($data['grps']);
144
145        $USERINFO                               = $data;
146        $_SERVER['REMOTE_USER']                 = $data['user'];
147        $_SESSION[DOKU_COOKIE]['auth']['user']  = $data['user'];
148        $_SESSION[DOKU_COOKIE]['auth']['pass']  = $data['pass'];
149        $_SESSION[DOKU_COOKIE]['auth']['info']  = $USERINFO;
150        $_SESSION[DOKU_COOKIE]['auth']['buid']  = auth_browseruid();
151        $_SESSION[DOKU_COOKIE]['auth']['time']  = time();
152        $_SESSION[DOKU_COOKIE]['auth']['oauth'] = $service;
153    }
154
155    /**
156     * Unset additional stuff in session on logout
157     */
158    public function logOff() {
159        parent::logOff();
160
161        if(isset($_SESSION[DOKU_COOKIE]['auth']['buid'])) {
162            unset($_SESSION[DOKU_COOKIE]['auth']['buid']);
163        }
164        if(isset($_SESSION[DOKU_COOKIE]['auth']['time'])) {
165            unset($_SESSION[DOKU_COOKIE]['auth']['time']);
166        }
167        if(isset($_SESSION[DOKU_COOKIE]['auth']['oauth'])) {
168            unset($_SESSION[DOKU_COOKIE]['auth']['oauth']);
169        }
170    }
171
172    /**
173     * Find a user by his email address
174     *
175     * @param $mail
176     * @return bool|string
177     */
178    protected function getUserByEmail($mail) {
179        if($this->users === null) $this->_loadUserData();
180        $mail = strtolower($mail);
181
182        foreach($this->users as $user => $uinfo) {
183            if(strtolower($uinfo['mail']) == $mail) return $user;
184        }
185
186        return false;
187    }
188
189    /**
190     * Enhance function to check aainst duplicate emails
191     *
192     * @param string $user
193     * @param string $pwd
194     * @param string $name
195     * @param string $mail
196     * @param null   $grps
197     * @return bool|null|string
198     */
199    public function createUser($user, $pwd, $name, $mail, $grps = null) {
200        if($this->getUserByEmail($mail)) {
201            msg($this->getLang('emailduplicate'), -1);
202            return false;
203        }
204
205        return parent::createUser($user, $pwd, $name, $mail, $grps);
206    }
207
208    /**
209     * Enhance function to check aainst duplicate emails
210     *
211     * @param string $user
212     * @param array  $changes
213     * @return bool
214     */
215    public function modifyUser($user, $changes) {
216        global $conf;
217
218        if(isset($changes['mail'])) {
219            $found = $this->getUserByEmail($changes['mail']);
220            if($found != $user) {
221                msg($this->getLang('emailduplicate'), -1);
222                return false;
223            }
224        }
225
226        $ok = parent::modifyUser($user, $changes);
227
228        // refresh session cache
229        touch($conf['cachedir'].'/sessionpurge');
230
231        return $ok;
232    }
233
234}
235
236// vim:ts=4:sw=4:et: