180852c15SAndreas Gohr<?php 280852c15SAndreas Gohr/** 380852c15SAndreas Gohr * DokuWiki Plugin oauth (Auth Component) 480852c15SAndreas Gohr * 580852c15SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 680852c15SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 780852c15SAndreas Gohr */ 880852c15SAndreas Gohr 980852c15SAndreas Gohr// must be run within Dokuwiki 1080852c15SAndreas Gohrif(!defined('DOKU_INC')) die(); 1180852c15SAndreas Gohr 12f10e09e2SAndreas Gohrclass auth_plugin_oauth extends auth_plugin_authplain { 1380852c15SAndreas Gohr 14f866280eSAndreas Gohr /** 15f866280eSAndreas Gohr * Constructor 16f866280eSAndreas Gohr * 17f866280eSAndreas Gohr * Sets capabilities. 18f866280eSAndreas Gohr */ 1980852c15SAndreas Gohr public function __construct() { 20f10e09e2SAndreas Gohr parent::__construct(); 2180852c15SAndreas Gohr 22f10e09e2SAndreas Gohr $this->cando['external'] = true; 2380852c15SAndreas Gohr } 2480852c15SAndreas Gohr 25f866280eSAndreas Gohr /** 26f866280eSAndreas Gohr * Handle the login 27f866280eSAndreas Gohr * 28f866280eSAndreas Gohr * This either trusts the session data (if any), processes the second oAuth step or simply 29f866280eSAndreas Gohr * executes a normal plugin against local users. 30f866280eSAndreas Gohr * 31f866280eSAndreas Gohr * @param string $user 32f866280eSAndreas Gohr * @param string $pass 33f866280eSAndreas Gohr * @param bool $sticky 34f866280eSAndreas Gohr * @return bool 35f866280eSAndreas Gohr */ 36f10e09e2SAndreas Gohr function trustExternal($user, $pass, $sticky = false) { 37a7a8f46aSAndreas Gohr global $conf; 38a7a8f46aSAndreas Gohr global $USERINFO; 3980852c15SAndreas Gohr 402e94f0b8SAndreas Gohr // are we in login progress? 412e94f0b8SAndreas Gohr if(isset($_SESSION[DOKU_COOKIE]['oauth-inprogress'])) { 422e94f0b8SAndreas Gohr $servicename = $_SESSION[DOKU_COOKIE]['oauth-inprogress']['service']; 432e94f0b8SAndreas Gohr $page = $_SESSION[DOKU_COOKIE]['oauth-inprogress']['id']; 442e94f0b8SAndreas Gohr 452e94f0b8SAndreas Gohr unset($_SESSION[DOKU_COOKIE]['oauth-inprogress']); 462e94f0b8SAndreas Gohr } 4780852c15SAndreas Gohr 48a7a8f46aSAndreas Gohr // check session for existing oAuth login data 49a7a8f46aSAndreas Gohr $session = $_SESSION[DOKU_COOKIE]['auth']; 502e94f0b8SAndreas Gohr if(!isset($servicename) && isset($session['oauth'])) { 51a7a8f46aSAndreas Gohr $servicename = $session['oauth']; 52a7a8f46aSAndreas Gohr // check if session data is still considered valid 53f2e164b0SMichael Große if ($this->isSessionValid($session)) { 54a7a8f46aSAndreas Gohr $_SERVER['REMOTE_USER'] = $session['user']; 55a7a8f46aSAndreas Gohr $USERINFO = $session['info']; 5680852c15SAndreas Gohr return true; 57f10e09e2SAndreas Gohr } 5880852c15SAndreas Gohr } 5980852c15SAndreas Gohr 60a7a8f46aSAndreas Gohr // either we're in oauth login or a previous log needs to be rechecked 612e94f0b8SAndreas Gohr if(isset($servicename)) { 62a7a8f46aSAndreas Gohr /** @var helper_plugin_oauth $hlp */ 63a7a8f46aSAndreas Gohr $hlp = plugin_load('helper', 'oauth'); 64a7a8f46aSAndreas Gohr $service = $hlp->loadService($servicename); 65a7a8f46aSAndreas Gohr if(is_null($service)) return false; 66a7a8f46aSAndreas Gohr 67a7a8f46aSAndreas Gohr if($service->checkToken()) { 68a7a8f46aSAndreas Gohr $uinfo = $service->getUser(); 69*9928f5efSMichael Große $ok = $this->processUser($uinfo, $servicename); 70caa5ded4SAndreas Gohr if (!$ok) { 71caa5ded4SAndreas Gohr return false; 72caa5ded4SAndreas Gohr } 73a7a8f46aSAndreas Gohr $this->setUserSession($uinfo, $servicename); 74*9928f5efSMichael Große $this->setUserCookie($user, $sticky, $servicename); 754485a349SMichael Große if(isset($page)) { 764485a349SMichael Große send_redirect(wl($page)); 774485a349SMichael Große } 78a7a8f46aSAndreas Gohr return true; 79213f4618SMichael Große } else { 80213f4618SMichael Große $this->relogin($servicename); 81a7a8f46aSAndreas Gohr } 82a7a8f46aSAndreas Gohr 83936b9c9cSMichael Große unset($_SESSION[DOKU_COOKIE]['auth']); 84a7a8f46aSAndreas Gohr return false; // something went wrong during oAuth login 85213f4618SMichael Große } elseif (isset($_COOKIE[DOKU_COOKIE])) { 86213f4618SMichael Große global $INPUT; 87213f4618SMichael Große //try cookie 88213f4618SMichael Große list($cookieuser, $cookiesticky, $auth, $servicename) = explode('|', $_COOKIE[DOKU_COOKIE]); 89213f4618SMichael Große $cookieuser = base64_decode($cookieuser, true); 90213f4618SMichael Große $auth = base64_decode($auth, true); 91213f4618SMichael Große $servicename = base64_decode($servicename, true); 92213f4618SMichael Große if ($auth === 'oauth') { 93213f4618SMichael Große $this->relogin($servicename); 94213f4618SMichael Große } 9580852c15SAndreas Gohr } 9680852c15SAndreas Gohr 97a7a8f46aSAndreas Gohr // do the "normal" plain auth login via form 98a7a8f46aSAndreas Gohr return auth_login($user, $pass, $sticky); 99a7a8f46aSAndreas Gohr } 10080852c15SAndreas Gohr 101f2e164b0SMichael Große /** 102f2e164b0SMichael Große * @param array $session cookie auth session 103f2e164b0SMichael Große * 104f2e164b0SMichael Große * @return bool 105f2e164b0SMichael Große */ 106f2e164b0SMichael Große protected function isSessionValid ($session) { 107f2e164b0SMichael Große /** @var helper_plugin_oauth $hlp */ 108f2e164b0SMichael Große $hlp = plugin_load('helper', 'oauth'); 109f2e164b0SMichael Große if ($hlp->validBrowserID($session)) { 110f2e164b0SMichael Große if (!$hlp->isSessionTimedOut($session)) { 111f2e164b0SMichael Große return true; 112f2e164b0SMichael Große } elseif (!($hlp->isGETRequest() && $hlp->isDokuPHP())) { 113f2e164b0SMichael Große // only force a recheck on a timed-out session during a GET request on the main script doku.php 114f2e164b0SMichael Große return true; 115f2e164b0SMichael Große } 116f2e164b0SMichael Große } 117f2e164b0SMichael Große return false; 118f2e164b0SMichael Große } 119f2e164b0SMichael Große 120213f4618SMichael Große protected function relogin($servicename) { 121213f4618SMichael Große global $INPUT; 122213f4618SMichael Große 123213f4618SMichael Große /** @var helper_plugin_oauth $hlp */ 124213f4618SMichael Große $hlp = plugin_load('helper', 'oauth'); 125213f4618SMichael Große $service = $hlp->loadService($servicename); 126213f4618SMichael Große if(is_null($service)) return false; 127213f4618SMichael Große 128213f4618SMichael Große // remember service in session 129213f4618SMichael Große session_start(); 130213f4618SMichael Große $_SESSION[DOKU_COOKIE]['oauth-inprogress']['service'] = $servicename; 131213f4618SMichael Große $_SESSION[DOKU_COOKIE]['oauth-inprogress']['id'] = $INPUT->str('id'); 132213f4618SMichael Große 13309623faaSMichael Große $_SESSION[DOKU_COOKIE]['oauth-done']['$_REQUEST'] = $_REQUEST; 134213f4618SMichael Große 135213f4618SMichael Große if (is_array($INPUT->post->param('do'))) { 136213f4618SMichael Große $doPost = key($INPUT->post->arr('do')); 137213f4618SMichael Große } else { 138213f4618SMichael Große $doPost = $INPUT->post->str('do'); 139213f4618SMichael Große } 140213f4618SMichael Große $doGet = $INPUT->get->str('do'); 141213f4618SMichael Große if (!empty($doPost)) { 142213f4618SMichael Große $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doPost; 143213f4618SMichael Große } elseif (!empty($doGet)) { 144213f4618SMichael Große $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doGet; 145213f4618SMichael Große } 146213f4618SMichael Große 147213f4618SMichael Große session_write_close(); 148213f4618SMichael Große 149213f4618SMichael Große $service->login(); 150213f4618SMichael Große } 151213f4618SMichael Große 152a7a8f46aSAndreas Gohr /** 153a7a8f46aSAndreas Gohr * @param array $data 154a7a8f46aSAndreas Gohr * @param string $service 155a7a8f46aSAndreas Gohr */ 156a7a8f46aSAndreas Gohr protected function setUserSession($data, $service) { 157a7a8f46aSAndreas Gohr global $USERINFO; 158a7a8f46aSAndreas Gohr global $conf; 159a7a8f46aSAndreas Gohr 160a7a8f46aSAndreas Gohr // set up groups 161a7a8f46aSAndreas Gohr if(!is_array($data['grps'])) { 162a7a8f46aSAndreas Gohr $data['grps'] = array(); 163a7a8f46aSAndreas Gohr } 164a7a8f46aSAndreas Gohr $data['grps'][] = $this->cleanGroup($service); 165f866280eSAndreas Gohr $data['grps'] = array_unique($data['grps']); 16680852c15SAndreas Gohr 167f10e09e2SAndreas Gohr $USERINFO = $data; 168f10e09e2SAndreas Gohr $_SERVER['REMOTE_USER'] = $data['user']; 169f10e09e2SAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['user'] = $data['user']; 170f10e09e2SAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['pass'] = $data['pass']; 171f10e09e2SAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO; 172a7a8f46aSAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid(); 173a7a8f46aSAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['time'] = time(); 174a7a8f46aSAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['oauth'] = $service; 17580852c15SAndreas Gohr } 17680852c15SAndreas Gohr 177f866280eSAndreas Gohr /** 178e32c3607SAndreas Gohr * Unset additional stuff in session on logout 179e32c3607SAndreas Gohr */ 180e32c3607SAndreas Gohr public function logOff() { 181e32c3607SAndreas Gohr parent::logOff(); 182e32c3607SAndreas Gohr 183e32c3607SAndreas Gohr if(isset($_SESSION[DOKU_COOKIE]['auth']['buid'])) { 184e32c3607SAndreas Gohr unset($_SESSION[DOKU_COOKIE]['auth']['buid']); 185e32c3607SAndreas Gohr } 186e32c3607SAndreas Gohr if(isset($_SESSION[DOKU_COOKIE]['auth']['time'])) { 187e32c3607SAndreas Gohr unset($_SESSION[DOKU_COOKIE]['auth']['time']); 188e32c3607SAndreas Gohr } 189e32c3607SAndreas Gohr if(isset($_SESSION[DOKU_COOKIE]['auth']['oauth'])) { 190e32c3607SAndreas Gohr unset($_SESSION[DOKU_COOKIE]['auth']['oauth']); 191e32c3607SAndreas Gohr } 192e32c3607SAndreas Gohr } 193e32c3607SAndreas Gohr 194e32c3607SAndreas Gohr /** 195f866280eSAndreas Gohr * Find a user by his email address 196f866280eSAndreas Gohr * 197f866280eSAndreas Gohr * @param $mail 198f866280eSAndreas Gohr * @return bool|string 199f866280eSAndreas Gohr */ 20038378fbbSAndreas Gohr protected function getUserByEmail($mail) { 201f866280eSAndreas Gohr if($this->users === null) $this->_loadUserData(); 20238378fbbSAndreas Gohr $mail = strtolower($mail); 203f866280eSAndreas Gohr 204f866280eSAndreas Gohr foreach($this->users as $user => $uinfo) { 205f866280eSAndreas Gohr if(strtolower($uinfo['mail']) == $mail) return $user; 20638378fbbSAndreas Gohr } 20738378fbbSAndreas Gohr 208f866280eSAndreas Gohr return false; 209f866280eSAndreas Gohr } 21038378fbbSAndreas Gohr 211f866280eSAndreas Gohr /** 212f07c7607SMichael Große * Enhance function to check against duplicate emails 213f866280eSAndreas Gohr * 214f866280eSAndreas Gohr * @param string $user 215f866280eSAndreas Gohr * @param string $pwd 216f866280eSAndreas Gohr * @param string $name 217f866280eSAndreas Gohr * @param string $mail 218f866280eSAndreas Gohr * @param null $grps 219f866280eSAndreas Gohr * @return bool|null|string 220f866280eSAndreas Gohr */ 221f866280eSAndreas Gohr public function createUser($user, $pwd, $name, $mail, $grps = null) { 222f866280eSAndreas Gohr if($this->getUserByEmail($mail)) { 223f866280eSAndreas Gohr msg($this->getLang('emailduplicate'), -1); 224f866280eSAndreas Gohr return false; 225f866280eSAndreas Gohr } 226f866280eSAndreas Gohr 22738378fbbSAndreas Gohr return parent::createUser($user, $pwd, $name, $mail, $grps); 22838378fbbSAndreas Gohr } 22938378fbbSAndreas Gohr 230f866280eSAndreas Gohr /** 231f866280eSAndreas Gohr * Enhance function to check aainst duplicate emails 232f866280eSAndreas Gohr * 233f866280eSAndreas Gohr * @param string $user 234f866280eSAndreas Gohr * @param array $changes 235f866280eSAndreas Gohr * @return bool 236f866280eSAndreas Gohr */ 23738378fbbSAndreas Gohr public function modifyUser($user, $changes) { 2383c0138dbSAndreas Gohr global $conf; 2393c0138dbSAndreas Gohr 2403c0138dbSAndreas Gohr if(isset($changes['mail'])) { 2413c0138dbSAndreas Gohr $found = $this->getUserByEmail($changes['mail']); 2423c0138dbSAndreas Gohr if($found != $user) { 243f866280eSAndreas Gohr msg($this->getLang('emailduplicate'), -1); 244f866280eSAndreas Gohr return false; 245f866280eSAndreas Gohr } 2463c0138dbSAndreas Gohr } 24738378fbbSAndreas Gohr 2483c0138dbSAndreas Gohr $ok = parent::modifyUser($user, $changes); 2493c0138dbSAndreas Gohr 2503c0138dbSAndreas Gohr // refresh session cache 2513c0138dbSAndreas Gohr touch($conf['cachedir'] . '/sessionpurge'); 2523c0138dbSAndreas Gohr 2533c0138dbSAndreas Gohr return $ok; 25438378fbbSAndreas Gohr } 25538378fbbSAndreas Gohr 256f07c7607SMichael Große /** 257f07c7607SMichael Große * new user, create him - making sure the login is unique by adding a number if needed 258f07c7607SMichael Große * 259*9928f5efSMichael Große * @param array $uinfo user info received from the oAuth service 260*9928f5efSMichael Große * @param string $servicename 261f07c7607SMichael Große * 262f07c7607SMichael Große * @return bool 263f07c7607SMichael Große */ 264*9928f5efSMichael Große protected function addUser(&$uinfo, $servicename) { 265f07c7607SMichael Große global $conf; 266f07c7607SMichael Große $user = $uinfo['user']; 267f07c7607SMichael Große $count = ''; 268f07c7607SMichael Große while($this->getUserData($user . $count)) { 269f07c7607SMichael Große if($count) { 270f07c7607SMichael Große $count++; 271f07c7607SMichael Große } else { 272f07c7607SMichael Große $count = 1; 273f07c7607SMichael Große } 274f07c7607SMichael Große } 275f07c7607SMichael Große $user = $user . $count; 276f07c7607SMichael Große $uinfo['user'] = $user; 277f07c7607SMichael Große $groups_on_creation = array(); 278f07c7607SMichael Große $groups_on_creation[] = $conf['defaultgroup']; 279f07c7607SMichael Große $groups_on_creation[] = $this->cleanGroup($servicename); // add service as group 280f07c7607SMichael Große $uinfo['grps'] = array_merge((array) $uinfo['grps'], $groups_on_creation); 281f07c7607SMichael Große 282f07c7607SMichael Große $ok = $this->triggerUserMod( 283f07c7607SMichael Große 'create', 284f07c7607SMichael Große array($user, auth_pwgen($user), $uinfo['name'], $uinfo['mail'], $groups_on_creation,) 285f07c7607SMichael Große ); 286f07c7607SMichael Große if(!$ok) { 287f07c7607SMichael Große return false; 288f07c7607SMichael Große } 289f07c7607SMichael Große 290f07c7607SMichael Große // send notification about the new user 291f07c7607SMichael Große $subscription = new Subscription(); 292f07c7607SMichael Große $subscription->send_register($user, $uinfo['name'], $uinfo['mail']); 293f07c7607SMichael Große return true; 294f07c7607SMichael Große } 295f07c7607SMichael Große 296*9928f5efSMichael Große /** 297*9928f5efSMichael Große * process the user and update the $uinfo array 298*9928f5efSMichael Große * 299*9928f5efSMichael Große * @param $uinfo 300*9928f5efSMichael Große * @param $servicename 301*9928f5efSMichael Große * 302*9928f5efSMichael Große * @return bool 303*9928f5efSMichael Große */ 304*9928f5efSMichael Große protected function processUser(&$uinfo, $servicename) { 305*9928f5efSMichael Große $uinfo['user'] = $this->cleanUser((string) $uinfo['user']); 306*9928f5efSMichael Große if(!$uinfo['name']) $uinfo['name'] = $uinfo['user']; 307*9928f5efSMichael Große 308*9928f5efSMichael Große if(!$uinfo['user'] || !$uinfo['mail']) { 309*9928f5efSMichael Große msg("$servicename did not provide the needed user info. Can't log you in", -1); 310*9928f5efSMichael Große return false; 311*9928f5efSMichael Große } 312*9928f5efSMichael Große 313*9928f5efSMichael Große // see if the user is known already 314*9928f5efSMichael Große $user = $this->getUserByEmail($uinfo['mail']); 315*9928f5efSMichael Große if($user) { 316*9928f5efSMichael Große $sinfo = $this->getUserData($user); 317*9928f5efSMichael Große // check if the user allowed access via this service 318*9928f5efSMichael Große if(!in_array($this->cleanGroup($servicename), $sinfo['grps'])) { 319*9928f5efSMichael Große msg(sprintf($this->getLang('authnotenabled'), $servicename), -1); 320*9928f5efSMichael Große return false; 321*9928f5efSMichael Große } 322*9928f5efSMichael Große $uinfo['user'] = $user; 323*9928f5efSMichael Große $uinfo['name'] = $sinfo['name']; 324*9928f5efSMichael Große $uinfo['grps'] = array_merge((array) $uinfo['grps'], $sinfo['grps']); 325*9928f5efSMichael Große } elseif(actionOK('register')) { 326*9928f5efSMichael Große $ok = $this->addUser($uinfo, $servicename); 327*9928f5efSMichael Große if(!$ok) { 328*9928f5efSMichael Große msg('something went wrong creating your user account. please try again later.', -1); 329*9928f5efSMichael Große return false; 330*9928f5efSMichael Große } 331*9928f5efSMichael Große } else { 332*9928f5efSMichael Große msg($this->getLang('addUser not possible'), -1); 333*9928f5efSMichael Große return false; 334*9928f5efSMichael Große } 335*9928f5efSMichael Große return true; 336*9928f5efSMichael Große } 337*9928f5efSMichael Große 338*9928f5efSMichael Große /** 339*9928f5efSMichael Große * @param string $user 340*9928f5efSMichael Große * @param string $sticky 341*9928f5efSMichael Große * @param string $servicename 342*9928f5efSMichael Große */ 343*9928f5efSMichael Große private function setUserCookie($user, $sticky, $servicename) { 344*9928f5efSMichael Große $cookie = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode('oauth').'|'.base64_encode($servicename); 345*9928f5efSMichael Große $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir']; 346*9928f5efSMichael Große $time = $sticky ? (time() + 60 * 60 * 24 * 365) : 0; 347*9928f5efSMichael Große setcookie(DOKU_COOKIE,$cookie, $time, $cookieDir, '',($conf['securecookie'] && is_ssl()), true); 348*9928f5efSMichael Große } 349*9928f5efSMichael Große 35080852c15SAndreas Gohr} 35180852c15SAndreas Gohr 35280852c15SAndreas Gohr// vim:ts=4:sw=4:et: 353