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()) { 68213f4618SMichael Große 69213f4618SMichael Große 70a7a8f46aSAndreas Gohr $uinfo = $service->getUser(); 71f866280eSAndreas Gohr 721025aad7SAndreas Gohr $uinfo['user'] = $this->cleanUser((string) $uinfo['user']); 731025aad7SAndreas Gohr if(!$uinfo['name']) $uinfo['name'] = $uinfo['user']; 741025aad7SAndreas Gohr 751025aad7SAndreas Gohr if(!$uinfo['user'] || !$uinfo['mail']) { 761025aad7SAndreas Gohr msg("$servicename did not provide the needed user info. Can't log you in", -1); 771025aad7SAndreas Gohr return false; 781025aad7SAndreas Gohr } 791025aad7SAndreas Gohr 80f866280eSAndreas Gohr // see if the user is known already 81f866280eSAndreas Gohr $user = $this->getUserByEmail($uinfo['mail']); 82f866280eSAndreas Gohr if($user) { 83f866280eSAndreas Gohr $sinfo = $this->getUserData($user); 843c0138dbSAndreas Gohr // check if the user allowed access via this service 853c0138dbSAndreas Gohr if(!in_array($this->cleanGroup($servicename), $sinfo['grps'])) { 863c0138dbSAndreas Gohr msg(sprintf($this->getLang('authnotenabled'), $servicename), -1); 873c0138dbSAndreas Gohr return false; 883c0138dbSAndreas Gohr } 89f866280eSAndreas Gohr $uinfo['user'] = $user; 90f866280eSAndreas Gohr $uinfo['name'] = $sinfo['name']; 91f866280eSAndreas Gohr $uinfo['grps'] = array_merge((array) $uinfo['grps'], $sinfo['grps']); 92ebf681d1SMichael Große } elseif (actionOK('register')) { 93*f07c7607SMichael Große $ok = $this->addUser($uinfo, $servicename); 94caa5ded4SAndreas Gohr if (!$ok) { 95caa5ded4SAndreas Gohr msg('something went wrong creating your user account. please try again later.', -1); 96caa5ded4SAndreas Gohr return false; 97caa5ded4SAndreas Gohr } 980aa332ffSMichael Große } else { 99*f07c7607SMichael Große msg($this->getLang('addUser not possible'), -1); 1000aa332ffSMichael Große return false; 101f866280eSAndreas Gohr } 102f866280eSAndreas Gohr 103f866280eSAndreas Gohr // set user session 104a7a8f46aSAndreas Gohr $this->setUserSession($uinfo, $servicename); 105213f4618SMichael Große 106213f4618SMichael Große $cookie = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode('oauth').'|'.base64_encode($servicename); 107213f4618SMichael Große $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir']; 108213f4618SMichael Große $time = $sticky ? (time() + 60 * 60 * 24 * 365) : 0; 109213f4618SMichael Große setcookie(DOKU_COOKIE,$cookie, $time, $cookieDir, '',($conf['securecookie'] && is_ssl()), true); 110213f4618SMichael Große 1114485a349SMichael Große if(isset($page)) { 1124485a349SMichael Große send_redirect(wl($page)); 1134485a349SMichael Große } 114a7a8f46aSAndreas Gohr return true; 115213f4618SMichael Große } else { 116213f4618SMichael Große $this->relogin($servicename); 117a7a8f46aSAndreas Gohr } 118a7a8f46aSAndreas Gohr 119936b9c9cSMichael Große unset($_SESSION[DOKU_COOKIE]['auth']); 120a7a8f46aSAndreas Gohr return false; // something went wrong during oAuth login 121213f4618SMichael Große } elseif (isset($_COOKIE[DOKU_COOKIE])) { 122213f4618SMichael Große global $INPUT; 123213f4618SMichael Große //try cookie 124213f4618SMichael Große list($cookieuser, $cookiesticky, $auth, $servicename) = explode('|', $_COOKIE[DOKU_COOKIE]); 125213f4618SMichael Große $cookieuser = base64_decode($cookieuser, true); 126213f4618SMichael Große $auth = base64_decode($auth, true); 127213f4618SMichael Große $servicename = base64_decode($servicename, true); 128213f4618SMichael Große if ($auth === 'oauth') { 129213f4618SMichael Große $this->relogin($servicename); 130213f4618SMichael Große } 13180852c15SAndreas Gohr } 13280852c15SAndreas Gohr 133a7a8f46aSAndreas Gohr // do the "normal" plain auth login via form 134a7a8f46aSAndreas Gohr return auth_login($user, $pass, $sticky); 135a7a8f46aSAndreas Gohr } 13680852c15SAndreas Gohr 137f2e164b0SMichael Große /** 138f2e164b0SMichael Große * @param array $session cookie auth session 139f2e164b0SMichael Große * 140f2e164b0SMichael Große * @return bool 141f2e164b0SMichael Große */ 142f2e164b0SMichael Große protected function isSessionValid ($session) { 143f2e164b0SMichael Große /** @var helper_plugin_oauth $hlp */ 144f2e164b0SMichael Große $hlp = plugin_load('helper', 'oauth'); 145f2e164b0SMichael Große if ($hlp->validBrowserID($session)) { 146f2e164b0SMichael Große if (!$hlp->isSessionTimedOut($session)) { 147f2e164b0SMichael Große return true; 148f2e164b0SMichael Große } elseif (!($hlp->isGETRequest() && $hlp->isDokuPHP())) { 149f2e164b0SMichael Große // only force a recheck on a timed-out session during a GET request on the main script doku.php 150f2e164b0SMichael Große return true; 151f2e164b0SMichael Große } 152f2e164b0SMichael Große } 153f2e164b0SMichael Große return false; 154f2e164b0SMichael Große } 155f2e164b0SMichael Große 156213f4618SMichael Große protected function relogin($servicename) { 157213f4618SMichael Große global $INPUT; 158213f4618SMichael Große 159213f4618SMichael Große /** @var helper_plugin_oauth $hlp */ 160213f4618SMichael Große $hlp = plugin_load('helper', 'oauth'); 161213f4618SMichael Große $service = $hlp->loadService($servicename); 162213f4618SMichael Große if(is_null($service)) return false; 163213f4618SMichael Große 164213f4618SMichael Große // remember service in session 165213f4618SMichael Große session_start(); 166213f4618SMichael Große $_SESSION[DOKU_COOKIE]['oauth-inprogress']['service'] = $servicename; 167213f4618SMichael Große $_SESSION[DOKU_COOKIE]['oauth-inprogress']['id'] = $INPUT->str('id'); 168213f4618SMichael Große 16909623faaSMichael Große $_SESSION[DOKU_COOKIE]['oauth-done']['$_REQUEST'] = $_REQUEST; 170213f4618SMichael Große 171213f4618SMichael Große if (is_array($INPUT->post->param('do'))) { 172213f4618SMichael Große $doPost = key($INPUT->post->arr('do')); 173213f4618SMichael Große } else { 174213f4618SMichael Große $doPost = $INPUT->post->str('do'); 175213f4618SMichael Große } 176213f4618SMichael Große $doGet = $INPUT->get->str('do'); 177213f4618SMichael Große if (!empty($doPost)) { 178213f4618SMichael Große $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doPost; 179213f4618SMichael Große } elseif (!empty($doGet)) { 180213f4618SMichael Große $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doGet; 181213f4618SMichael Große } 182213f4618SMichael Große 183213f4618SMichael Große session_write_close(); 184213f4618SMichael Große 185213f4618SMichael Große $service->login(); 186213f4618SMichael Große } 187213f4618SMichael Große 188a7a8f46aSAndreas Gohr /** 189a7a8f46aSAndreas Gohr * @param array $data 190a7a8f46aSAndreas Gohr * @param string $service 191a7a8f46aSAndreas Gohr */ 192a7a8f46aSAndreas Gohr protected function setUserSession($data, $service) { 193a7a8f46aSAndreas Gohr global $USERINFO; 194a7a8f46aSAndreas Gohr global $conf; 195a7a8f46aSAndreas Gohr 196a7a8f46aSAndreas Gohr // set up groups 197a7a8f46aSAndreas Gohr if(!is_array($data['grps'])) { 198a7a8f46aSAndreas Gohr $data['grps'] = array(); 199a7a8f46aSAndreas Gohr } 200a7a8f46aSAndreas Gohr $data['grps'][] = $this->cleanGroup($service); 201f866280eSAndreas Gohr $data['grps'] = array_unique($data['grps']); 20280852c15SAndreas Gohr 203f10e09e2SAndreas Gohr $USERINFO = $data; 204f10e09e2SAndreas Gohr $_SERVER['REMOTE_USER'] = $data['user']; 205f10e09e2SAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['user'] = $data['user']; 206f10e09e2SAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['pass'] = $data['pass']; 207f10e09e2SAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO; 208a7a8f46aSAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid(); 209a7a8f46aSAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['time'] = time(); 210a7a8f46aSAndreas Gohr $_SESSION[DOKU_COOKIE]['auth']['oauth'] = $service; 21180852c15SAndreas Gohr } 21280852c15SAndreas Gohr 213f866280eSAndreas Gohr /** 214e32c3607SAndreas Gohr * Unset additional stuff in session on logout 215e32c3607SAndreas Gohr */ 216e32c3607SAndreas Gohr public function logOff() { 217e32c3607SAndreas Gohr parent::logOff(); 218e32c3607SAndreas Gohr 219e32c3607SAndreas Gohr if(isset($_SESSION[DOKU_COOKIE]['auth']['buid'])) { 220e32c3607SAndreas Gohr unset($_SESSION[DOKU_COOKIE]['auth']['buid']); 221e32c3607SAndreas Gohr } 222e32c3607SAndreas Gohr if(isset($_SESSION[DOKU_COOKIE]['auth']['time'])) { 223e32c3607SAndreas Gohr unset($_SESSION[DOKU_COOKIE]['auth']['time']); 224e32c3607SAndreas Gohr } 225e32c3607SAndreas Gohr if(isset($_SESSION[DOKU_COOKIE]['auth']['oauth'])) { 226e32c3607SAndreas Gohr unset($_SESSION[DOKU_COOKIE]['auth']['oauth']); 227e32c3607SAndreas Gohr } 228e32c3607SAndreas Gohr } 229e32c3607SAndreas Gohr 230e32c3607SAndreas Gohr /** 231f866280eSAndreas Gohr * Find a user by his email address 232f866280eSAndreas Gohr * 233f866280eSAndreas Gohr * @param $mail 234f866280eSAndreas Gohr * @return bool|string 235f866280eSAndreas Gohr */ 23638378fbbSAndreas Gohr protected function getUserByEmail($mail) { 237f866280eSAndreas Gohr if($this->users === null) $this->_loadUserData(); 23838378fbbSAndreas Gohr $mail = strtolower($mail); 239f866280eSAndreas Gohr 240f866280eSAndreas Gohr foreach($this->users as $user => $uinfo) { 241f866280eSAndreas Gohr if(strtolower($uinfo['mail']) == $mail) return $user; 24238378fbbSAndreas Gohr } 24338378fbbSAndreas Gohr 244f866280eSAndreas Gohr return false; 245f866280eSAndreas Gohr } 24638378fbbSAndreas Gohr 247f866280eSAndreas Gohr /** 248*f07c7607SMichael Große * Enhance function to check against duplicate emails 249f866280eSAndreas Gohr * 250f866280eSAndreas Gohr * @param string $user 251f866280eSAndreas Gohr * @param string $pwd 252f866280eSAndreas Gohr * @param string $name 253f866280eSAndreas Gohr * @param string $mail 254f866280eSAndreas Gohr * @param null $grps 255f866280eSAndreas Gohr * @return bool|null|string 256f866280eSAndreas Gohr */ 257f866280eSAndreas Gohr public function createUser($user, $pwd, $name, $mail, $grps = null) { 258f866280eSAndreas Gohr if($this->getUserByEmail($mail)) { 259f866280eSAndreas Gohr msg($this->getLang('emailduplicate'), -1); 260f866280eSAndreas Gohr return false; 261f866280eSAndreas Gohr } 262f866280eSAndreas Gohr 26338378fbbSAndreas Gohr return parent::createUser($user, $pwd, $name, $mail, $grps); 26438378fbbSAndreas Gohr } 26538378fbbSAndreas Gohr 266f866280eSAndreas Gohr /** 267f866280eSAndreas Gohr * Enhance function to check aainst duplicate emails 268f866280eSAndreas Gohr * 269f866280eSAndreas Gohr * @param string $user 270f866280eSAndreas Gohr * @param array $changes 271f866280eSAndreas Gohr * @return bool 272f866280eSAndreas Gohr */ 27338378fbbSAndreas Gohr public function modifyUser($user, $changes) { 2743c0138dbSAndreas Gohr global $conf; 2753c0138dbSAndreas Gohr 2763c0138dbSAndreas Gohr if(isset($changes['mail'])) { 2773c0138dbSAndreas Gohr $found = $this->getUserByEmail($changes['mail']); 2783c0138dbSAndreas Gohr if($found != $user) { 279f866280eSAndreas Gohr msg($this->getLang('emailduplicate'), -1); 280f866280eSAndreas Gohr return false; 281f866280eSAndreas Gohr } 2823c0138dbSAndreas Gohr } 28338378fbbSAndreas Gohr 2843c0138dbSAndreas Gohr $ok = parent::modifyUser($user, $changes); 2853c0138dbSAndreas Gohr 2863c0138dbSAndreas Gohr // refresh session cache 2873c0138dbSAndreas Gohr touch($conf['cachedir'] . '/sessionpurge'); 2883c0138dbSAndreas Gohr 2893c0138dbSAndreas Gohr return $ok; 29038378fbbSAndreas Gohr } 29138378fbbSAndreas Gohr 292*f07c7607SMichael Große /** 293*f07c7607SMichael Große * new user, create him - making sure the login is unique by adding a number if needed 294*f07c7607SMichael Große * 295*f07c7607SMichael Große * @param $uinfo 296*f07c7607SMichael Große * @param $servicename 297*f07c7607SMichael Große * 298*f07c7607SMichael Große * @return bool 299*f07c7607SMichael Große */ 300*f07c7607SMichael Große protected function addUser($uinfo, $servicename) { 301*f07c7607SMichael Große global $conf; 302*f07c7607SMichael Große $user = $uinfo['user']; 303*f07c7607SMichael Große $count = ''; 304*f07c7607SMichael Große while($this->getUserData($user . $count)) { 305*f07c7607SMichael Große if($count) { 306*f07c7607SMichael Große $count++; 307*f07c7607SMichael Große } else { 308*f07c7607SMichael Große $count = 1; 309*f07c7607SMichael Große } 310*f07c7607SMichael Große } 311*f07c7607SMichael Große $user = $user . $count; 312*f07c7607SMichael Große $uinfo['user'] = $user; 313*f07c7607SMichael Große $groups_on_creation = array(); 314*f07c7607SMichael Große $groups_on_creation[] = $conf['defaultgroup']; 315*f07c7607SMichael Große $groups_on_creation[] = $this->cleanGroup($servicename); // add service as group 316*f07c7607SMichael Große $uinfo['grps'] = array_merge((array) $uinfo['grps'], $groups_on_creation); 317*f07c7607SMichael Große 318*f07c7607SMichael Große $ok = $this->triggerUserMod( 319*f07c7607SMichael Große 'create', 320*f07c7607SMichael Große array($user, auth_pwgen($user), $uinfo['name'], $uinfo['mail'], $groups_on_creation,) 321*f07c7607SMichael Große ); 322*f07c7607SMichael Große if(!$ok) { 323*f07c7607SMichael Große return false; 324*f07c7607SMichael Große } 325*f07c7607SMichael Große 326*f07c7607SMichael Große // send notification about the new user 327*f07c7607SMichael Große $subscription = new Subscription(); 328*f07c7607SMichael Große $subscription->send_register($user, $uinfo['name'], $uinfo['mail']); 329*f07c7607SMichael Große return true; 330*f07c7607SMichael Große } 331*f07c7607SMichael Große 33280852c15SAndreas Gohr} 33380852c15SAndreas Gohr 33480852c15SAndreas Gohr// vim:ts=4:sw=4:et: 335