180852c15SAndreas Gohr<?php 23e7ac5b1SAndreas Gohr 3e170f465SAndreas Gohruse dokuwiki\plugin\oauth\OAuthManager; 4e170f465SAndreas Gohruse dokuwiki\plugin\oauth\Session; 5e170f465SAndreas Gohruse dokuwiki\Subscriptions\RegistrationSubscriptionSender; 6e170f465SAndreas Gohruse OAuth\Common\Exception\Exception as OAuthException; 7e170f465SAndreas Gohr 880852c15SAndreas Gohr/** 980852c15SAndreas Gohr * DokuWiki Plugin oauth (Auth Component) 1080852c15SAndreas Gohr * 1180852c15SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 1280852c15SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 1380852c15SAndreas Gohr */ 143e7ac5b1SAndreas Gohrclass auth_plugin_oauth extends auth_plugin_authplain 153e7ac5b1SAndreas Gohr{ 16e170f465SAndreas Gohr /** @var helper_plugin_oauth */ 17e170f465SAndreas Gohr protected $hlp; 18e170f465SAndreas Gohr 19a1fa007aSNaoto Kobayashi /** @var OAuthManager */ 20a1fa007aSNaoto Kobayashi protected $om; 21a1fa007aSNaoto Kobayashi 22e170f465SAndreas Gohr // region standard auth methods 2380852c15SAndreas Gohr 243e7ac5b1SAndreas Gohr /** @inheritDoc */ 253e7ac5b1SAndreas Gohr public function __construct() 263e7ac5b1SAndreas Gohr { 27f10e09e2SAndreas Gohr parent::__construct(); 28f10e09e2SAndreas Gohr $this->cando['external'] = true; 29e170f465SAndreas Gohr $this->hlp = $this->loadHelper('oauth'); 3080852c15SAndreas Gohr } 3180852c15SAndreas Gohr 323e7ac5b1SAndreas Gohr /** @inheritDoc */ 33311a6606SAnna Dabrowska public function trustExternal($user, $pass, $sticky = false) 343e7ac5b1SAndreas Gohr { 35a02a5d81SAnna Dabrowska global $INPUT; 36438dcc52SMichael Grosse 378523e9d0SAndreas Gohr // handle redirects from farmer to animal wiki instances 388523e9d0SAndreas Gohr if ($INPUT->has('state') && plugin_load('helper', 'farmer')) { 39d9be1cb5SAnna Dabrowska $this->handleFarmState($INPUT->str('state')); 40438dcc52SMichael Grosse } 4180852c15SAndreas Gohr 4274b4d4a4SAndreas Gohr try { 4374b4d4a4SAndreas Gohr // either oauth or "normal" plain auth login via form 44a1fa007aSNaoto Kobayashi $this->om = new OAuthManager(); 45568565b9SAndreas Gohr if ($this->om->continueFlow()) return true; 461cdd3f90SAndreas Gohr if ($this->getConf('singleService')) { 471cdd3f90SAndreas Gohr return false; // no normal login in singleService mode 481cdd3f90SAndreas Gohr } 49568565b9SAndreas Gohr return null; // triggers the normal auth_login() 50e170f465SAndreas Gohr } catch (OAuthException $e) { 51e170f465SAndreas Gohr $this->hlp->showException($e); 5228002081SAndreas Gohr auth_logoff(); // clears all session and cookie data 5374b4d4a4SAndreas Gohr return false; 54a7a8f46aSAndreas Gohr } 55a7a8f46aSAndreas Gohr } 5680852c15SAndreas Gohr 57f2e164b0SMichael Große /** 58*fe49fd82SAndreas Gohr * Enforce oauth login for certain email domains 59*fe49fd82SAndreas Gohr * 60*fe49fd82SAndreas Gohr * @inheritdoc 61*fe49fd82SAndreas Gohr */ 62*fe49fd82SAndreas Gohr public function checkPass($user, $pass) 63*fe49fd82SAndreas Gohr { 64*fe49fd82SAndreas Gohr $ok = parent::checkPass($user, $pass); 65*fe49fd82SAndreas Gohr if(!$ok) return $ok; 66*fe49fd82SAndreas Gohr $domains = $this->hlp->getEnforcedDomains(); 67*fe49fd82SAndreas Gohr if($domains === []) return $ok; 68*fe49fd82SAndreas Gohr 69*fe49fd82SAndreas Gohr if($this->hlp->checkMail($this->getUserData($user)['mail'], $domains)) { 70*fe49fd82SAndreas Gohr global $lang; 71*fe49fd82SAndreas Gohr // we overwrite the standard bad password message with our own 72*fe49fd82SAndreas Gohr $lang['badlogin'] = $this->getLang('eMailEnforced'); 73*fe49fd82SAndreas Gohr return false; 74*fe49fd82SAndreas Gohr } 75*fe49fd82SAndreas Gohr return $ok; 76*fe49fd82SAndreas Gohr } 77*fe49fd82SAndreas Gohr 78*fe49fd82SAndreas Gohr 79*fe49fd82SAndreas Gohr /** 80311a6606SAnna Dabrowska * Enhance function to check against duplicate emails 81311a6606SAnna Dabrowska * 82e170f465SAndreas Gohr * @inheritdoc 83311a6606SAnna Dabrowska */ 84311a6606SAnna Dabrowska public function createUser($user, $pwd, $name, $mail, $grps = null) 85311a6606SAnna Dabrowska { 86311a6606SAnna Dabrowska if ($this->getUserByEmail($mail)) { 87311a6606SAnna Dabrowska msg($this->getLang('emailduplicate'), -1); 88311a6606SAnna Dabrowska return false; 89311a6606SAnna Dabrowska } 90311a6606SAnna Dabrowska 91311a6606SAnna Dabrowska return parent::createUser($user, $pwd, $name, $mail, $grps); 92311a6606SAnna Dabrowska } 93311a6606SAnna Dabrowska 94311a6606SAnna Dabrowska /** 95311a6606SAnna Dabrowska * Enhance function to check against duplicate emails 96311a6606SAnna Dabrowska * 97e170f465SAndreas Gohr * @inheritdoc 98311a6606SAnna Dabrowska */ 99311a6606SAnna Dabrowska public function modifyUser($user, $changes) 100311a6606SAnna Dabrowska { 101311a6606SAnna Dabrowska global $conf; 102311a6606SAnna Dabrowska 103311a6606SAnna Dabrowska if (isset($changes['mail'])) { 104311a6606SAnna Dabrowska $found = $this->getUserByEmail($changes['mail']); 105311a6606SAnna Dabrowska if ($found && $found != $user) { 106311a6606SAnna Dabrowska msg($this->getLang('emailduplicate'), -1); 107311a6606SAnna Dabrowska return false; 108311a6606SAnna Dabrowska } 109311a6606SAnna Dabrowska } 110311a6606SAnna Dabrowska 111311a6606SAnna Dabrowska $ok = parent::modifyUser($user, $changes); 112311a6606SAnna Dabrowska 113311a6606SAnna Dabrowska // refresh session cache 114311a6606SAnna Dabrowska touch($conf['cachedir'] . '/sessionpurge'); 115311a6606SAnna Dabrowska return $ok; 116311a6606SAnna Dabrowska } 117311a6606SAnna Dabrowska 118311a6606SAnna Dabrowska /** 119311a6606SAnna Dabrowska * Unset additional stuff in session on logout 120311a6606SAnna Dabrowska */ 121311a6606SAnna Dabrowska public function logOff() 122311a6606SAnna Dabrowska { 123311a6606SAnna Dabrowska parent::logOff(); 124a1fa007aSNaoto Kobayashi if (isset($this->om)) { 125a1fa007aSNaoto Kobayashi $this->om->logout(); 126a1fa007aSNaoto Kobayashi } 127e170f465SAndreas Gohr (Session::getInstance())->clear(); 128311a6606SAnna Dabrowska } 129311a6606SAnna Dabrowska 130e170f465SAndreas Gohr // endregion 131e170f465SAndreas Gohr 132311a6606SAnna Dabrowska /** 133e170f465SAndreas Gohr * Register a new user logged in by oauth 134f2e164b0SMichael Große * 135e170f465SAndreas Gohr * It ensures the username is unique, by adding a number if needed. 136e170f465SAndreas Gohr * Default and service name groups are set here. 137e170f465SAndreas Gohr * Registration notifications are triggered. 138a02a5d81SAnna Dabrowska * 139e170f465SAndreas Gohr * @param array $userinfo This will be updated with the new username 140b2b9fbc7SMichael Große * @param string $servicename 141b2b9fbc7SMichael Große * 142b2b9fbc7SMichael Große * @return bool 143e170f465SAndreas Gohr * @todo - should this be part of the OAuthManager class instead? 144b2b9fbc7SMichael Große */ 145e170f465SAndreas Gohr public function registerOAuthUser(&$userinfo, $servicename) 1463e7ac5b1SAndreas Gohr { 147b2b9fbc7SMichael Große global $conf; 148a02a5d81SAnna Dabrowska $user = $userinfo['user']; 149b2b9fbc7SMichael Große $count = ''; 150b2b9fbc7SMichael Große while ($this->getUserData($user . $count)) { 151b2b9fbc7SMichael Große if ($count) { 152b2b9fbc7SMichael Große $count++; 153b2b9fbc7SMichael Große } else { 154b2b9fbc7SMichael Große $count = 1; 155b2b9fbc7SMichael Große } 156b2b9fbc7SMichael Große } 157290e9b1fSAndreas Gohr $user .= $count; 158a02a5d81SAnna Dabrowska $userinfo['user'] = $user; 159e170f465SAndreas Gohr $groups_on_creation = []; 160b2b9fbc7SMichael Große $groups_on_creation[] = $conf['defaultgroup']; 161b2b9fbc7SMichael Große $groups_on_creation[] = $this->cleanGroup($servicename); // add service as group 162a02a5d81SAnna Dabrowska $userinfo['grps'] = array_merge((array)$userinfo['grps'], $groups_on_creation); 163b2b9fbc7SMichael Große 164e170f465SAndreas Gohr // the password set here will remain unknown to the user 165b2b9fbc7SMichael Große $ok = $this->triggerUserMod( 166b2b9fbc7SMichael Große 'create', 167e170f465SAndreas Gohr [ 168e170f465SAndreas Gohr $user, 169e170f465SAndreas Gohr auth_pwgen($user), 170e170f465SAndreas Gohr $userinfo['name'], 171e170f465SAndreas Gohr $userinfo['mail'], 1724928b245SAndreas Gohr $userinfo['grps'], 173e170f465SAndreas Gohr ] 174b2b9fbc7SMichael Große ); 175b2b9fbc7SMichael Große if (!$ok) { 176b2b9fbc7SMichael Große return false; 177b2b9fbc7SMichael Große } 178b2b9fbc7SMichael Große 179e170f465SAndreas Gohr // send notification about the new user 180e170f465SAndreas Gohr $subscriptionSender = new RegistrationSubscriptionSender(); 181e170f465SAndreas Gohr $subscriptionSender->sendRegister($user, $userinfo['name'], $userinfo['mail']); 182e170f465SAndreas Gohr 183b2b9fbc7SMichael Große return true; 184b2b9fbc7SMichael Große } 185b2b9fbc7SMichael Große 186b2b9fbc7SMichael Große /** 187a02a5d81SAnna Dabrowska * Find a user by email address 188b2b9fbc7SMichael Große * 189b2b9fbc7SMichael Große * @param $mail 190b2b9fbc7SMichael Große * @return bool|string 191b2b9fbc7SMichael Große */ 19274b4d4a4SAndreas Gohr public function getUserByEmail($mail) 1933e7ac5b1SAndreas Gohr { 1948b214edcSAndreas Gohr if ($this->users === null) { 1958b214edcSAndreas Gohr $this->loadUserData(); 1968b214edcSAndreas Gohr } 197b2b9fbc7SMichael Große $mail = strtolower($mail); 198b2b9fbc7SMichael Große 199a02a5d81SAnna Dabrowska foreach ($this->users as $user => $userinfo) { 200290e9b1fSAndreas Gohr if (strtolower($userinfo['mail']) === $mail) return $user; 201b2b9fbc7SMichael Große } 202b2b9fbc7SMichael Große 203b2b9fbc7SMichael Große return false; 204b2b9fbc7SMichael Große } 205b2b9fbc7SMichael Große 206b2b9fbc7SMichael Große /** 2072a8b22d5SAndreas Gohr * Fall back to plain auth strings 2082a8b22d5SAndreas Gohr * 2092a8b22d5SAndreas Gohr * @inheritdoc 2102a8b22d5SAndreas Gohr */ 2112a8b22d5SAndreas Gohr public function getLang($id) 2122a8b22d5SAndreas Gohr { 2132a8b22d5SAndreas Gohr $result = parent::getLang($id); 2142a8b22d5SAndreas Gohr if ($result) return $result; 2152a8b22d5SAndreas Gohr 2162a8b22d5SAndreas Gohr $parent = new auth_plugin_authplain(); 2172a8b22d5SAndreas Gohr return $parent->getLang($id); 2182a8b22d5SAndreas Gohr } 2192a8b22d5SAndreas Gohr 2202a8b22d5SAndreas Gohr /** 221b8ca6a42SAnna Dabrowska * Farmer plugin support 222b8ca6a42SAnna Dabrowska * 223b8ca6a42SAnna Dabrowska * When coming back to farmer instance via OAUTH redirectURI, we need to redirect again 224b8ca6a42SAnna Dabrowska * to a proper animal instance detected from $state 225b2b9fbc7SMichael Große * 226311a6606SAnna Dabrowska * @param $state 227b2b9fbc7SMichael Große */ 228e170f465SAndreas Gohr protected function handleFarmState($state) 2293e7ac5b1SAndreas Gohr { 230311a6606SAnna Dabrowska /** @var \helper_plugin_farmer $farmer */ 231311a6606SAnna Dabrowska $farmer = plugin_load('helper', 'farmer', false, true); 232311a6606SAnna Dabrowska $data = json_decode(base64_decode(urldecode($state))); 233311a6606SAnna Dabrowska if (empty($data->animal) || $farmer->getAnimal() == $data->animal) { 234311a6606SAnna Dabrowska return; 235827232fcSMichael Große } 236311a6606SAnna Dabrowska $animal = $data->animal; 237311a6606SAnna Dabrowska $allAnimals = $farmer->getAllAnimals(); 238311a6606SAnna Dabrowska if (!in_array($animal, $allAnimals)) { 239311a6606SAnna Dabrowska msg('Animal ' . $animal . ' does not exist!'); 240311a6606SAnna Dabrowska return; 241827232fcSMichael Große } 242311a6606SAnna Dabrowska global $INPUT; 243311a6606SAnna Dabrowska $url = $farmer->getAnimalURL($animal) . '/doku.php?' . $INPUT->server->str('QUERY_STRING'); 244311a6606SAnna Dabrowska send_redirect($url); 245b2b9fbc7SMichael Große } 246b2b9fbc7SMichael Große} 247