1<?php
2// must be run within Dokuwiki
3if(!defined('DOKU_INC')) die();
4define('GOOGLE_API_DIR', dirname(__FILE__).'/google/');
5
6global $conf;
7// define cookie and session id, append server port when securecookie is configured
8if (!defined('AUTHGOOGLE_COOKIE')) define('AUTHGOOGLE_COOKIE', 'SPGG'.md5(DOKU_REL.(($conf['securecookie'])?$_SERVER['SERVER_PORT']:'')));
9
10/**
11 * Google Auth 2.0 authentication backend
12 *
13 * @author sentryperm@gmail.com
14 */
15class auth_plugin_authgoogle extends auth_plugin_authplain  {
16
17    public function __construct() {
18        global $config_cascade;
19        parent::__construct();
20
21        // fix if acl no used
22        $this->success = true;
23
24        $this->cando['external'] = true;
25        $this->cando['logout'] = true;
26    }
27
28    function trustExternal($user, $pass, $sticky = false) {
29	global $USERINFO, $ID;
30
31        //get user info in session
32        if (!empty($_SESSION[DOKU_COOKIE]['authgoogle']['info'])) {
33            $USERINFO['name'] = $_SESSION[DOKU_COOKIE]['authgoogle']['info']['name'];
34            $USERINFO['mail'] = $_SESSION[DOKU_COOKIE]['authgoogle']['info']['mail'];
35            $USERINFO['grps'] = $_SESSION[DOKU_COOKIE]['authgoogle']['info']['grps'];
36            $USERINFO['is_google'] = $_SESSION[DOKU_COOKIE]['authgoogle']['info']['is_google'];
37            $_SERVER['REMOTE_USER'] = $_SESSION[DOKU_COOKIE]['authgoogle']['user'];
38            return true;
39	}
40
41        //get form login info
42        if(!empty($user)){
43            if($this->checkPass($user,$pass)){
44                $uinfo  = $this->getUserData($user);
45
46                //set user info
47                $USERINFO['name'] = $uinfo['name'];
48                $USERINFO['mail'] = $uinfo['email'];
49                $USERINFO['grps'] = $uinfo['grps'];
50                $USERINFO['pass'] = $pass;
51
52                //save data in session
53                $_SERVER['REMOTE_USER'] = $uinfo['name'];
54                $_SESSION[DOKU_COOKIE]['authgoogle']['user'] = $uinfo['name'];
55                $_SESSION[DOKU_COOKIE]['authgoogle']['info'] = $USERINFO;
56
57                return true;
58            }else{
59                //invalid credentials - log off
60                msg($this->getLang('badlogin'),-1);
61                return false;
62            }
63        }
64
65        //if token saved in cookies - get it
66        if ($_COOKIE[AUTHGOOGLE_COOKIE]) {
67            $_SESSION[DOKU_COOKIE]['authgoogle']['token'] = $_COOKIE[AUTHGOOGLE_COOKIE];
68        }
69
70	//set our referer for redirection, if we're hitting login
71	if (!empty($_SERVER['HTTP_REFERER'])) {
72		$_SESSION[DOKU_COOKIE]['authgoogle']['referer'] = $_SERVER['HTTP_REFERER'];
73	}
74
75        //google auth
76        require_once GOOGLE_API_DIR.'/Google_Client.php';
77        require_once GOOGLE_API_DIR.'/contrib/Google_Oauth2Service.php';
78
79        $client = new Google_Client();
80        $client->setApplicationName("Google Application");
81        $client->setClientId($this->getConf('client_id'));
82        $client->setClientSecret($this->getConf('client_secret'));
83        $client->setRedirectUri(wl('start',array('do'=>'login'),true, '&'));
84        $client->setAccessType('online');
85        $client->setApprovalPrompt('auto');
86
87        $oauth2 = new Google_Oauth2Service($client);
88        //get code from google redirect link
89        if (isset($_GET['code'])) {
90            //get token
91            try {
92                $client->authenticate($_GET['code']);
93                //save token in session
94                $_SESSION[DOKU_COOKIE]['authgoogle']['token'] = $client->getAccessToken();
95                //save token in cookies
96                $this->_updateCookie($_SESSION[DOKU_COOKIE]['authgoogle']['token'], time() + 60 * 60 * 24 * 365);
97                //redirect to login page
98                header("Location: ".wl('start', array('do'=>'login'), true, '&'));
99                die();
100            } catch (Exception $e) {
101                msg('Auth Google Error: '.$e->getMessage());
102            }
103        }
104        //save state and auth_url in session
105        $_SESSION[DOKU_COOKIE]['authgoogle']['state'] = $state;
106        $_SESSION[DOKU_COOKIE]['authgoogle']['auth_url'] = $client->createAuthUrl();
107        $_SESSION[DOKU_COOKIE]['authgoogle']['auth_url'] .= "&state=".$state;
108
109        //set token in client
110        if (isset($_SESSION[DOKU_COOKIE]['authgoogle']['token'])) {
111            try {
112                $client->setAccessToken($_SESSION[DOKU_COOKIE]['authgoogle']['token']);
113            } catch (Exception $e){
114                $this->logOff();
115                return false;
116            }
117        }
118
119        //if successed auth
120        if ($client->getAccessToken()) {
121
122            // If the access token is expired, ask the user to login again
123            if($client->isAccessTokenExpired()) {
124                $authUrl = $client->createAuthUrl();
125                header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));
126            }
127
128            $user = $oauth2->userinfo->get();
129            $email = filter_var($user['email'], FILTER_SANITIZE_EMAIL);
130            //$img = filter_var($user['picture'], FILTER_VALIDATE_URL);
131            //$personMarkup = "$email<div><img src='$img?sz=50'></div>";
132
133            //Check verify email in google
134            if (!$user['verified_email']) {
135                msg('Auth Google Error: '.$email.' not verifed in google account');
136                $this->logOff();
137                return false;
138            }
139
140            //check email in list allows
141            if (!$this->_check_email_domain($email)) {
142                msg('Auth Google Error: access denied for '.$email);
143                $this->logOff();
144                return false;
145            }
146
147            //create and update user in base
148            $login = 'google'.$user['id'];
149            $udata = $this->getUserData($login);
150            if (!$udata) {
151                //default groups
152                $grps = null;
153                if ($this->getConf('default_groups')) $grps = explode(' ', $this->getConf('default_groups'));
154                //create user
155                $this->createUser($login, md5(rand().$login), $user['name'], $email, $grps);
156                $udata = $this->getUserData($login);
157            } elseif ($udata['name'] != $user['name'] || $udata['email'] != $email) {
158                //update user
159                $this->modifyUser($login, array('name'=>$user['name'], 'email'=>$email));
160            }
161
162            //set user info
163            $USERINFO['pass'] = "";
164            $USERINFO['name'] = $user['name'];
165            $USERINFO['mail'] = $email;
166            $USERINFO['grps'] = $udata['grps'];
167            $USERINFO['is_google'] = true;
168            $_SERVER['REMOTE_USER'] = $user['name'];
169
170            //save user info in session
171            $_SESSION[DOKU_COOKIE]['authgoogle']['user'] = $_SERVER['REMOTE_USER'];
172            $_SESSION[DOKU_COOKIE]['authgoogle']['info'] = $USERINFO;
173
174            // update token
175            $_SESSION['token'] = $client->getAccessToken();
176
177            //if login page - redirect to original referer or, if none, start page.
178            if (isset($_GET['do']) && $_GET['do']=='login') {
179		$referer = $_SESSION[DOKU_COOKIE]['authgoogle']['referer'];
180	    	$redirect = empty($referer) ? wl('start', '', true) : $referer;
181		header("Location: ".$referer);
182	    }
183
184            return true;
185        } else {
186            //no auth
187        }
188
189        return false;
190    }
191
192    function _check_email_domain($email) {
193        //check email in allow domains
194        if ($this->getConf('allowed_domains')) {
195            $domains = preg_split("/[ ]+/is", $this->getConf('allowed_domains'));
196            foreach ($domains as $domain) {
197                $domain = trim($domain);
198                //all domains
199                if ($domain == '*') return true;
200                //email
201                if ($email == $domain) return true;
202                //domain
203                if (preg_match("/^\\*@([^@ ]+)/is", $domain, $m)) {
204                    if (preg_match("/@([^@ ]+)$/is", $email, $n)) {
205                        if ($m[1] == $n[1]) return true;
206                    }
207                }
208            }
209        }
210        return false;
211    }
212
213    function logOff(){
214        unset($_SESSION[DOKU_COOKIE]['authgoogle']['token']);
215        unset($_SESSION[DOKU_COOKIE]['authgoogle']['user']);
216        unset($_SESSION[DOKU_COOKIE]['authgoogle']['info']);
217        // clear the cookie
218        $this->_updateCookie('', time() - 600000);
219    }
220
221    function _updateCookie($value, $time) {
222        global $conf;
223
224        $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
225        if (version_compare(PHP_VERSION, '5.2.0', '>')) {
226            setcookie(AUTHGOOGLE_COOKIE, $value, $time, $cookieDir, '', ($conf['securecookie'] && is_ssl()), true);
227        } else {
228            setcookie(AUTHGOOGLE_COOKIE, $value, $time, $cookieDir, '', ($conf['securecookie'] && is_ssl()));
229        }
230    }
231
232    function cleanUser($user){
233
234        /* Sometimes system ask for a user in email format and we need to replace @ for _
235         In this case, the user is not the logged in one. This happens mostly in  the admin tools*/
236        if(filter_var($user, FILTER_VALIDATE_EMAIL)){
237            return str_replace("@", "_",$user);
238        }
239        /* Sometimes system ask for $login info, that is generated in the registration process some lines above
240         In this case we return the same username
241        TODO: check with a regexp */
242        if(substr( $user, 0, 6 ) === "google"){
243            return $user;
244        }
245        /* When ACL checks the username, it ask for the name of the user (that can be a serious security bug)
246           so, if the system ask for the name of the current user, I send the email replacing @ for _ cause  the
247           user logged with googleauth can't change its email by hand.
248        */
249        if ($user == $_SESSION[DOKU_COOKIE]['authgoogle']['user'] ){
250            return str_replace("@", "_", $_SESSION[DOKU_COOKIE]['authgoogle']['info']['mail']);
251        }
252        /*Every other case return the same that you sent*/
253        return $user;
254    }
255}
256?>
257