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