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