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