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'); 64*827232fcSMichael Große 65*827232fcSMichael Große /** @var OAuth\Plugin\AbstractAdapter $service */ 66a7a8f46aSAndreas Gohr $service = $hlp->loadService($servicename); 67a7a8f46aSAndreas Gohr if(is_null($service)) return false; 68a7a8f46aSAndreas Gohr 69a7a8f46aSAndreas Gohr if($service->checkToken()) { 70*827232fcSMichael Große return $this->processLogin($sticky, $service, $servicename, $page); 71213f4618SMichael Große } else { 72213f4618SMichael Große $this->relogin($servicename); 73a7a8f46aSAndreas Gohr } 74a7a8f46aSAndreas Gohr 75936b9c9cSMichael Große unset($_SESSION[DOKU_COOKIE]['auth']); 76a7a8f46aSAndreas Gohr return false; // something went wrong during oAuth login 77213f4618SMichael Große } elseif (isset($_COOKIE[DOKU_COOKIE])) { 78213f4618SMichael Große global $INPUT; 79213f4618SMichael Große //try cookie 80213f4618SMichael Große list($cookieuser, $cookiesticky, $auth, $servicename) = explode('|', $_COOKIE[DOKU_COOKIE]); 81213f4618SMichael Große $cookieuser = base64_decode($cookieuser, true); 82213f4618SMichael Große $auth = base64_decode($auth, true); 83213f4618SMichael Große $servicename = base64_decode($servicename, true); 84213f4618SMichael Große if ($auth === 'oauth') { 85213f4618SMichael Große $this->relogin($servicename); 86213f4618SMichael Große } 8780852c15SAndreas Gohr } 8880852c15SAndreas Gohr 89a7a8f46aSAndreas Gohr // do the "normal" plain auth login via form 90a7a8f46aSAndreas Gohr return auth_login($user, $pass, $sticky); 91a7a8f46aSAndreas Gohr } 9280852c15SAndreas Gohr 93f2e164b0SMichael Große /** 94f2e164b0SMichael Große * @param array $session cookie auth session 95f2e164b0SMichael Große * 96f2e164b0SMichael Große * @return bool 97f2e164b0SMichael Große */ 98f2e164b0SMichael Große protected function isSessionValid ($session) { 99f2e164b0SMichael Große /** @var helper_plugin_oauth $hlp */ 100f2e164b0SMichael Große $hlp = plugin_load('helper', 'oauth'); 101f2e164b0SMichael Große if ($hlp->validBrowserID($session)) { 102f2e164b0SMichael Große if (!$hlp->isSessionTimedOut($session)) { 103f2e164b0SMichael Große return true; 104f2e164b0SMichael Große } elseif (!($hlp->isGETRequest() && $hlp->isDokuPHP())) { 105f2e164b0SMichael Große // only force a recheck on a timed-out session during a GET request on the main script doku.php 106f2e164b0SMichael Große return true; 107f2e164b0SMichael Große } 108f2e164b0SMichael Große } 109f2e164b0SMichael Große return false; 110f2e164b0SMichael Große } 111f2e164b0SMichael Große 112213f4618SMichael Große protected function relogin($servicename) { 113213f4618SMichael Große global $INPUT; 114213f4618SMichael Große 115213f4618SMichael Große /** @var helper_plugin_oauth $hlp */ 116213f4618SMichael Große $hlp = plugin_load('helper', 'oauth'); 117213f4618SMichael Große $service = $hlp->loadService($servicename); 118213f4618SMichael Große if(is_null($service)) return false; 119213f4618SMichael Große 120213f4618SMichael Große // remember service in session 121213f4618SMichael Große session_start(); 122213f4618SMichael Große $_SESSION[DOKU_COOKIE]['oauth-inprogress']['service'] = $servicename; 123213f4618SMichael Große $_SESSION[DOKU_COOKIE]['oauth-inprogress']['id'] = $INPUT->str('id'); 124213f4618SMichael Große 12509623faaSMichael Große $_SESSION[DOKU_COOKIE]['oauth-done']['$_REQUEST'] = $_REQUEST; 126213f4618SMichael Große 127213f4618SMichael Große if (is_array($INPUT->post->param('do'))) { 128213f4618SMichael Große $doPost = key($INPUT->post->arr('do')); 129213f4618SMichael Große } else { 130213f4618SMichael Große $doPost = $INPUT->post->str('do'); 131213f4618SMichael Große } 132213f4618SMichael Große $doGet = $INPUT->get->str('do'); 133213f4618SMichael Große if (!empty($doPost)) { 134213f4618SMichael Große $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doPost; 135213f4618SMichael Große } elseif (!empty($doGet)) { 136213f4618SMichael Große $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doGet; 137213f4618SMichael Große } 138213f4618SMichael Große 139213f4618SMichael Große session_write_close(); 140213f4618SMichael Große 141213f4618SMichael Große $service->login(); 142213f4618SMichael Große } 143213f4618SMichael Große 144a7a8f46aSAndreas Gohr /** 145a7a8f46aSAndreas Gohr * @param array $data 146a7a8f46aSAndreas Gohr * @param string $service 147a7a8f46aSAndreas Gohr */ 148a7a8f46aSAndreas Gohr protected function setUserSession($data, $service) { 149a7a8f46aSAndreas Gohr global $USERINFO; 150a7a8f46aSAndreas Gohr global $conf; 151a7a8f46aSAndreas Gohr 152a7a8f46aSAndreas Gohr // set up groups 153a7a8f46aSAndreas Gohr if(!is_array($data['grps'])) { 154a7a8f46aSAndreas Gohr $data['grps'] = array(); 155a7a8f46aSAndreas Gohr } 156a7a8f46aSAndreas Gohr $data['grps'][] = $this->cleanGroup($service); 157f866280eSAndreas Gohr $data['grps'] = array_unique($data['grps']); 15880852c15SAndreas Gohr 159f10e09e2SAndreas Gohr $USERINFO = $data; 160f10e09e2SAndreas Gohr $_SERVER['REMOTE_USER'] = $data['user']; 161f10e09e2SAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['user'] = $data['user']; 162f10e09e2SAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['pass'] = $data['pass']; 163f10e09e2SAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO; 164a7a8f46aSAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid(); 165a7a8f46aSAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['time'] = time(); 166a7a8f46aSAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['oauth'] = $service; 16780852c15SAndreas Gohr } 16880852c15SAndreas Gohr 169f866280eSAndreas Gohr /** 170e32c3607SAndreas Gohr * Unset additional stuff in session on logout 171e32c3607SAndreas Gohr */ 172e32c3607SAndreas Gohr public function logOff() { 173e32c3607SAndreas Gohr parent::logOff(); 174e32c3607SAndreas Gohr 175e32c3607SAndreas Gohr if(isset($_SESSION[DOKU_COOKIE]['auth']['buid'])) { 176e32c3607SAndreas Gohr unset($_SESSION[DOKU_COOKIE]['auth']['buid']); 177e32c3607SAndreas Gohr } 178e32c3607SAndreas Gohr if(isset($_SESSION[DOKU_COOKIE]['auth']['time'])) { 179e32c3607SAndreas Gohr unset($_SESSION[DOKU_COOKIE]['auth']['time']); 180e32c3607SAndreas Gohr } 181e32c3607SAndreas Gohr if(isset($_SESSION[DOKU_COOKIE]['auth']['oauth'])) { 182e32c3607SAndreas Gohr unset($_SESSION[DOKU_COOKIE]['auth']['oauth']); 183e32c3607SAndreas Gohr } 184e32c3607SAndreas Gohr } 185e32c3607SAndreas Gohr 186e32c3607SAndreas Gohr /** 187f866280eSAndreas Gohr * Find a user by his email address 188f866280eSAndreas Gohr * 189f866280eSAndreas Gohr * @param $mail 190f866280eSAndreas Gohr * @return bool|string 191f866280eSAndreas Gohr */ 19238378fbbSAndreas Gohr protected function getUserByEmail($mail) { 193f866280eSAndreas Gohr if($this->users === null) $this->_loadUserData(); 19438378fbbSAndreas Gohr $mail = strtolower($mail); 195f866280eSAndreas Gohr 196f866280eSAndreas Gohr foreach($this->users as $user => $uinfo) { 197f866280eSAndreas Gohr if(strtolower($uinfo['mail']) == $mail) return $user; 19838378fbbSAndreas Gohr } 19938378fbbSAndreas Gohr 200f866280eSAndreas Gohr return false; 201f866280eSAndreas Gohr } 20238378fbbSAndreas Gohr 203f866280eSAndreas Gohr /** 204f07c7607SMichael Große * Enhance function to check against duplicate emails 205f866280eSAndreas Gohr * 206f866280eSAndreas Gohr * @param string $user 207f866280eSAndreas Gohr * @param string $pwd 208f866280eSAndreas Gohr * @param string $name 209f866280eSAndreas Gohr * @param string $mail 210f866280eSAndreas Gohr * @param null $grps 211f866280eSAndreas Gohr * @return bool|null|string 212f866280eSAndreas Gohr */ 213f866280eSAndreas Gohr public function createUser($user, $pwd, $name, $mail, $grps = null) { 214f866280eSAndreas Gohr if($this->getUserByEmail($mail)) { 215f866280eSAndreas Gohr msg($this->getLang('emailduplicate'), -1); 216f866280eSAndreas Gohr return false; 217f866280eSAndreas Gohr } 218f866280eSAndreas Gohr 21938378fbbSAndreas Gohr return parent::createUser($user, $pwd, $name, $mail, $grps); 22038378fbbSAndreas Gohr } 22138378fbbSAndreas Gohr 222f866280eSAndreas Gohr /** 223f866280eSAndreas Gohr * Enhance function to check aainst duplicate emails 224f866280eSAndreas Gohr * 225f866280eSAndreas Gohr * @param string $user 226f866280eSAndreas Gohr * @param array $changes 227f866280eSAndreas Gohr * @return bool 228f866280eSAndreas Gohr */ 22938378fbbSAndreas Gohr public function modifyUser($user, $changes) { 2303c0138dbSAndreas Gohr global $conf; 2313c0138dbSAndreas Gohr 2323c0138dbSAndreas Gohr if(isset($changes['mail'])) { 2333c0138dbSAndreas Gohr $found = $this->getUserByEmail($changes['mail']); 2343c0138dbSAndreas Gohr if($found != $user) { 235f866280eSAndreas Gohr msg($this->getLang('emailduplicate'), -1); 236f866280eSAndreas Gohr return false; 237f866280eSAndreas Gohr } 2383c0138dbSAndreas Gohr } 23938378fbbSAndreas Gohr 2403c0138dbSAndreas Gohr $ok = parent::modifyUser($user, $changes); 2413c0138dbSAndreas Gohr 2423c0138dbSAndreas Gohr // refresh session cache 2433c0138dbSAndreas Gohr touch($conf['cachedir'] . '/sessionpurge'); 2443c0138dbSAndreas Gohr 2453c0138dbSAndreas Gohr return $ok; 24638378fbbSAndreas Gohr } 24738378fbbSAndreas Gohr 248f07c7607SMichael Große /** 249f07c7607SMichael Große * new user, create him - making sure the login is unique by adding a number if needed 250f07c7607SMichael Große * 2519928f5efSMichael Große * @param array $uinfo user info received from the oAuth service 2529928f5efSMichael Große * @param string $servicename 253f07c7607SMichael Große * 254f07c7607SMichael Große * @return bool 255f07c7607SMichael Große */ 2569928f5efSMichael Große protected function addUser(&$uinfo, $servicename) { 257f07c7607SMichael Große global $conf; 258f07c7607SMichael Große $user = $uinfo['user']; 259f07c7607SMichael Große $count = ''; 260f07c7607SMichael Große while($this->getUserData($user . $count)) { 261f07c7607SMichael Große if($count) { 262f07c7607SMichael Große $count++; 263f07c7607SMichael Große } else { 264f07c7607SMichael Große $count = 1; 265f07c7607SMichael Große } 266f07c7607SMichael Große } 267f07c7607SMichael Große $user = $user . $count; 268f07c7607SMichael Große $uinfo['user'] = $user; 269f07c7607SMichael Große $groups_on_creation = array(); 270f07c7607SMichael Große $groups_on_creation[] = $conf['defaultgroup']; 271f07c7607SMichael Große $groups_on_creation[] = $this->cleanGroup($servicename); // add service as group 272f07c7607SMichael Große $uinfo['grps'] = array_merge((array) $uinfo['grps'], $groups_on_creation); 273f07c7607SMichael Große 274f07c7607SMichael Große $ok = $this->triggerUserMod( 275f07c7607SMichael Große 'create', 276f07c7607SMichael Große array($user, auth_pwgen($user), $uinfo['name'], $uinfo['mail'], $groups_on_creation,) 277f07c7607SMichael Große ); 278f07c7607SMichael Große if(!$ok) { 279f07c7607SMichael Große return false; 280f07c7607SMichael Große } 281f07c7607SMichael Große 282f07c7607SMichael Große // send notification about the new user 283f07c7607SMichael Große $subscription = new Subscription(); 284f07c7607SMichael Große $subscription->send_register($user, $uinfo['name'], $uinfo['mail']); 285f07c7607SMichael Große return true; 286f07c7607SMichael Große } 287f07c7607SMichael Große 2889928f5efSMichael Große /** 2899928f5efSMichael Große * process the user and update the $uinfo array 2909928f5efSMichael Große * 2919928f5efSMichael Große * @param $uinfo 2929928f5efSMichael Große * @param $servicename 2939928f5efSMichael Große * 2949928f5efSMichael Große * @return bool 2959928f5efSMichael Große */ 2969928f5efSMichael Große protected function processUser(&$uinfo, $servicename) { 2979928f5efSMichael Große $uinfo['user'] = $this->cleanUser((string) $uinfo['user']); 2989928f5efSMichael Große if(!$uinfo['name']) $uinfo['name'] = $uinfo['user']; 2999928f5efSMichael Große 3009928f5efSMichael Große if(!$uinfo['user'] || !$uinfo['mail']) { 3019928f5efSMichael Große msg("$servicename did not provide the needed user info. Can't log you in", -1); 3029928f5efSMichael Große return false; 3039928f5efSMichael Große } 3049928f5efSMichael Große 3059928f5efSMichael Große // see if the user is known already 3069928f5efSMichael Große $user = $this->getUserByEmail($uinfo['mail']); 3079928f5efSMichael Große if($user) { 3089928f5efSMichael Große $sinfo = $this->getUserData($user); 3099928f5efSMichael Große // check if the user allowed access via this service 3109928f5efSMichael Große if(!in_array($this->cleanGroup($servicename), $sinfo['grps'])) { 3119928f5efSMichael Große msg(sprintf($this->getLang('authnotenabled'), $servicename), -1); 3129928f5efSMichael Große return false; 3139928f5efSMichael Große } 3149928f5efSMichael Große $uinfo['user'] = $user; 3159928f5efSMichael Große $uinfo['name'] = $sinfo['name']; 3169928f5efSMichael Große $uinfo['grps'] = array_merge((array) $uinfo['grps'], $sinfo['grps']); 3179928f5efSMichael Große } elseif(actionOK('register')) { 3189928f5efSMichael Große $ok = $this->addUser($uinfo, $servicename); 3199928f5efSMichael Große if(!$ok) { 3209928f5efSMichael Große msg('something went wrong creating your user account. please try again later.', -1); 3219928f5efSMichael Große return false; 3229928f5efSMichael Große } 3239928f5efSMichael Große } else { 3249928f5efSMichael Große msg($this->getLang('addUser not possible'), -1); 3259928f5efSMichael Große return false; 3269928f5efSMichael Große } 3279928f5efSMichael Große return true; 3289928f5efSMichael Große } 3299928f5efSMichael Große 3309928f5efSMichael Große /** 3319928f5efSMichael Große * @param string $user 3329928f5efSMichael Große * @param string $sticky 3339928f5efSMichael Große * @param string $servicename 3349928f5efSMichael Große */ 3359928f5efSMichael Große private function setUserCookie($user, $sticky, $servicename) { 3369928f5efSMichael Große $cookie = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode('oauth').'|'.base64_encode($servicename); 3379928f5efSMichael Große $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir']; 3389928f5efSMichael Große $time = $sticky ? (time() + 60 * 60 * 24 * 365) : 0; 3399928f5efSMichael Große setcookie(DOKU_COOKIE,$cookie, $time, $cookieDir, '',($conf['securecookie'] && is_ssl()), true); 3409928f5efSMichael Große } 3419928f5efSMichael Große 342*827232fcSMichael Große /** 343*827232fcSMichael Große * @param $sticky 344*827232fcSMichael Große * @param OAuth\Plugin\AbstractAdapter $service 345*827232fcSMichael Große * @param string $servicename 346*827232fcSMichael Große * @param string $page 347*827232fcSMichael Große * 348*827232fcSMichael Große * @return bool 349*827232fcSMichael Große */ 350*827232fcSMichael Große protected function processLogin($sticky, $service, $servicename, $page) { 351*827232fcSMichael Große $uinfo = $service->getUser(); 352*827232fcSMichael Große $ok = $this->processUser($uinfo, $servicename); 353*827232fcSMichael Große if(!$ok) { 354*827232fcSMichael Große return false; 355*827232fcSMichael Große } 356*827232fcSMichael Große $this->setUserSession($uinfo, $servicename); 357*827232fcSMichael Große $this->setUserCookie($uinfo['user'], $sticky, $servicename); 358*827232fcSMichael Große if(isset($page)) { 359*827232fcSMichael Große send_redirect(wl($page)); 360*827232fcSMichael Große } 361*827232fcSMichael Große return true; 362*827232fcSMichael Große } 363*827232fcSMichael Große 36480852c15SAndreas Gohr} 36580852c15SAndreas Gohr 36680852c15SAndreas Gohr// vim:ts=4:sw=4:et: 367