*/ // must be run within Dokuwiki if(!defined('DOKU_INC')) die(); class auth_plugin_evesso extends auth_plugin_authplain { /** * Constructor * * Sets capabilities. */ public function __construct() { parent::__construct(); $this->cando['external'] = true; } /** * Handle the login * * This either trusts the session data (if any), processes the second oAuth step or simply * executes a normal plugin against local users. * * @param string $user * @param string $pass * @param bool $sticky * @return bool */ function trustExternal($user, $pass, $sticky = false) { global $USERINFO, $INPUT; // check session for existing oAuth login data if(isset($_SESSION[DOKU_COOKIE]['auth']['oauth'])) { $session = $_SESSION[DOKU_COOKIE]['auth']; $servicename = $session['oauth']; // check if session data is still considered valid if ($this->isSessionValid($session)) { $_SERVER['REMOTE_USER'] = $session['user']; $USERINFO = $session['info']; return true; } } $existingLoginProcess = false; // are we in login progress? if(isset($_SESSION[DOKU_COOKIE]['oauth-inprogress'])) { $servicename = $_SESSION[DOKU_COOKIE]['oauth-inprogress']['service']; $page = $_SESSION[DOKU_COOKIE]['oauth-inprogress']['id']; if (isset($_SESSION[DOKU_COOKIE]['oauth-inprogress']['params'])) { $params = $_SESSION[DOKU_COOKIE]['oauth-inprogress']['params']; } else { $params = array(); } unset($_SESSION[DOKU_COOKIE]['oauth-inprogress']); $existingLoginProcess = true; } // either we're in oauth login or a previous log needs to be rechecked if(isset($servicename)) { /** @var helper_plugin_evesso $hlp */ $hlp = plugin_load('helper', 'evesso'); /** @var OAuth\Plugin\AbstractAdapter $service */ $service = $hlp->loadService($servicename); if(is_null($service)) { $this->cleanLogout(); return false; } if($service->checkToken()) { $ok = $this->processLogin($sticky, $hlp, $service, $servicename, $page, $params); if (!$ok) { $this->cleanLogout(); return false; } return true; } else { if ($existingLoginProcess) { msg($this->getLang('loginFailed'), -1); $this->cleanLogout(); return false; } else { // first time here $this->relogin($servicename); } } $this->cleanLogout(); return false; // something went wrong during oAuth login } elseif (isset($_COOKIE[DOKU_COOKIE])) { global $INPUT; //try cookie $cookie = explode('|', $_COOKIE[DOKU_COOKIE]); if (isset($cookie[2]) && isset($cookie[3])) { $auth = base64_decode($cookie[2], true); $servicename = base64_decode($cookie[3], true); if ($auth === 'oauth') { $this->relogin($servicename); } } } // do the "normal" plain auth login via form return auth_login($user, $pass, $sticky); } /** * @param array $session cookie auth session * * @return bool */ protected function isSessionValid ($session) { /** @var helper_plugin_evesso $hlp */ $hlp = plugin_load('helper', 'evesso'); if ($hlp->validBrowserID($session)) { if (!$hlp->isSessionTimedOut($session)) { return true; } elseif (!($hlp->isGETRequest() && $hlp->isDokuPHP())) { // only force a recheck on a timed-out session during a GET request on the main script doku.php return true; } } return false; } protected function relogin($servicename) { global $INPUT; /** @var helper_plugin_evesso $hlp */ $hlp = plugin_load('helper', 'evesso'); $service = $hlp->loadService($servicename); if(is_null($service)) return false; // remember service in session session_start(); $_SESSION[DOKU_COOKIE]['oauth-inprogress']['service'] = $servicename; $_SESSION[DOKU_COOKIE]['oauth-inprogress']['id'] = $INPUT->str('id'); $_SESSION[DOKU_COOKIE]['oauth-inprogress']['params'] = $_GET; $_SESSION[DOKU_COOKIE]['oauth-done']['$_REQUEST'] = $_REQUEST; if (is_array($INPUT->post->param('do'))) { $doPost = key($INPUT->post->arr('do')); } else { $doPost = $INPUT->post->str('do'); } $doGet = $INPUT->get->str('do'); if (!empty($doPost)) { $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doPost; } elseif (!empty($doGet)) { $_SESSION[DOKU_COOKIE]['oauth-done']['do'] = $doGet; } session_write_close(); $service->login(); } /** * @param $sticky * @param OAuth\Plugin\AbstractAdapter $service * @param string $servicename * @param string $page * @param array $params * * @return bool */ protected function processLogin($sticky, $hlp, $service, $servicename, $page, $params = array()) { $uinfo = $service->getUser(); $ok = $this->processUser($uinfo, $hlp, $servicename); if(!$ok) { return false; } $this->setUserSession($uinfo, $servicename); $this->setUserCookie($uinfo['user'], $sticky, $servicename); if(isset($page)) { if(!empty($params['id'])) unset($params['id']); send_redirect(wl($page, $params, false, '&')); } return true; } /** * process the user and update the $uinfo array * * @param $uinfo * @param $servicename * * @return bool */ protected function processUser(&$uinfo, $hlp, $servicename) { $uinfo['user'] = $this->cleanUser((string) $uinfo['user']); if(!$uinfo['name']) $uinfo['name'] = $uinfo['user']; if(!$uinfo['user'] || !$uinfo['mail']) { msg("$servicename did not provide the needed user info. Can't log you in", -1); return false; } // see if the user is known already $user = $this->getUserByEmail($uinfo['mail']); if($user) { $sinfo = $this->getUserData($user); // check if the user allowed access via this service if(!in_array($this->cleanServiceGroup($servicename), $sinfo['grps'])) { msg(sprintf($this->getLang('authnotenabled'), $servicename), -1); return false; } $uinfo['user'] = $user; $uinfo['name'] = $sinfo['name']; if ($this->haveEveGroups($uinfo['grps'])) { //Update groups //Remove all eve groups foreach($sinfo['grps'] as $group) { if($this->startsWith($group, helper_plugin_evesso::CORPORATION_PREFIX) || $this->startsWith($group, helper_plugin_evesso::ALLIANCE_PREFIX) || $this->startsWith($group, helper_plugin_evesso::FACTION_PREFIX) || $this->startsWith($group, "eve-") //Old Prefix ) { $idx = array_search($group, $sinfo['grps']); if($idx !== false) unset($sinfo['grps'][$idx]); } } //Merge groups $uinfo['grps'] = array_unique(array_merge((array) $uinfo['grps'], $sinfo['grps'])); } else { //Load groups $uinfo['grps'] = $sinfo['grps']; } //Update groups $this->modifyUser($user, array('grps' => $uinfo['grps'])); //Check group if (!$hlp->checkGroups($uinfo['grps'])) { msg($this->getLang("rejectedGroup"), -1); return false; } return true; //Existing valid user } elseif(actionOK('register') || $this->getConf('register-on-auth')) { //Check group before creation if (!$hlp->checkGroups($uinfo['grps'])) { msg($this->getLang("rejectedGroup"), -1); return false; } //Add user $ok = $this->addUser($uinfo, $servicename); if(!$ok) { msg('something went wrong creating your user account. please try again later.', -1); return false; } return true; //New valid user } //Not existing valid user and can not create a new user msg($this->getLang('addUserNotPossible'), -1); return false; } private function haveEveGroups($groups) { if (!isset($groups)) { return false; } foreach($groups as $group) { if($this->startsWith($group, helper_plugin_evesso::CORPORATION_PREFIX) || $this->startsWith($group, helper_plugin_evesso::ALLIANCE_PREFIX) || $this->startsWith($group, helper_plugin_evesso::FACTION_PREFIX) || $this->startsWith($group, "eve-") //Old Prefix ) { return true; } } return false; } private function startsWith($haystack, $needle) { $length = strlen($needle); return (substr($haystack, 0, $length) === $needle); } /** * new user, create him - making sure the login is unique by adding a number if needed * * @param array $uinfo user info received from the oAuth service * @param string $servicename * * @return bool */ protected function addUser(&$uinfo, $servicename) { global $conf; $user = $uinfo['user']; $count = ''; while($this->getUserData($user . $count)) { if($count) { $count++; } else { $count = 1; } } $user = $user . $count; $uinfo['user'] = $user; $groups_on_creation = array(); $groups_on_creation[] = $conf['defaultgroup']; $groups_on_creation[] = $this->cleanServiceGroup($servicename); // add service as group $uinfo['grps'] = array_merge((array) $uinfo['grps'], $groups_on_creation); $ok = $this->triggerUserMod( 'create', array($user, auth_pwgen($user), $uinfo['name'], $uinfo['mail'], $uinfo['grps'],) ); if(!$ok) { return false; } // send notification about the new user $subscription = new Subscription(); $subscription->send_register($user, $uinfo['name'], $uinfo['mail']); return true; } /** * Find a user by his email address * * @param $mail * @return bool|string */ protected function getUserByEmail($mail) { if (method_exists($this, "loadUserData")) { if($this->users === null) $this->loadUserData(); } else { if($this->users === null) $this->_loadUserData(); } $mail = strtolower($mail); foreach($this->users as $user => $uinfo) { if(strtolower($uinfo['mail']) == $mail) return $user; } return false; } /** * @param array $data * @param string $service */ protected function setUserSession($data, $service) { global $USERINFO; // set up groups if(!is_array($data['grps'])) { $data['grps'] = array(); } $data['grps'][] = $this->cleanServiceGroup($service); $data['grps'] = array_unique($data['grps']); $USERINFO = $data; $_SERVER['REMOTE_USER'] = $data['user']; $_SESSION[DOKU_COOKIE]['auth']['user'] = $data['user']; if (isset($data['pass'])) { $_SESSION[DOKU_COOKIE]['auth']['pass'] = $data['pass']; } $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO; $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid(); $_SESSION[DOKU_COOKIE]['auth']['time'] = time(); $_SESSION[DOKU_COOKIE]['auth']['oauth'] = $service; } /** * @param string $user * @param bool $sticky * @param string $servicename * @param int $validityPeriodInSeconds optional, per default 1 Year */ private function setUserCookie($user, $sticky, $servicename, $validityPeriodInSeconds = 31536000) { global $conf; $cookie = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode('oauth').'|'.base64_encode($servicename); $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir']; $time = $sticky ? (time() + $validityPeriodInSeconds) : 0; setcookie(DOKU_COOKIE,$cookie, $time, $cookieDir, '',($conf['securecookie'] && is_ssl()), true); } /** * Unset additional stuff in session on logout */ public function logOff() { parent::logOff(); $this->cleanLogout(); } /** * unset auth cookies and session information */ private function cleanLogout() { $hlp = plugin_load('helper', 'evesso'); $service = $hlp->loadService(); if (!is_null($service)) { $service->logout(); } if(isset($_SESSION[DOKU_COOKIE]['auth'])) { unset($_SESSION[DOKU_COOKIE]['auth']); } $this->setUserCookie('',true,'',-60); } /** * Enhance function to check against duplicate emails * * @param string $user * @param string $pwd * @param string $name * @param string $mail * @param null $grps * @return bool|null|string */ public function createUser($user, $pwd, $name, $mail, $grps = null) { if($this->getUserByEmail($mail)) { msg($this->getLang('emailduplicate'), -1); return false; } return parent::createUser($user, $pwd, $name, $mail, $grps); } /** * Enhance function to check against duplicate emails * * @param string $user * @param array $changes * @return bool */ public function modifyUser($user, $changes) { global $conf; if(isset($changes['mail'])) { $found = $this->getUserByEmail($changes['mail']); if($found != $user) { msg($this->getLang('emailduplicate'), -1); return false; } } $ok = parent::modifyUser($user, $changes); // refresh session cache touch($conf['cachedir'] . '/sessionpurge'); return $ok; } public function cleanServiceGroup($group) { return strtolower($this->cleanGroup($group)); } public function cleanGroup($group) { global $conf; return str_replace(':', $conf['sepchar'], $group); } } // vim:ts=4:sw=4:et: