1<?php 2 3namespace dokuwiki\plugin\oauth; 4 5use OAuth\Common\Http\Exception\TokenResponseException; 6 7/** 8 * Implements the flow control for oAuth 9 */ 10class OAuthManager 11{ 12 // region main flow 13 14 /** 15 * Explicitly starts the oauth flow by redirecting to IDP 16 * 17 * @throws Exception 18 * @throws TokenResponseException 19 */ 20 public function startFlow($servicename) 21 { 22 global $ID; 23 24 // generate a new GUID to identify this user 25 $guid = bin2hex(random_bytes(16)); 26 27 $session = Session::getInstance(); 28 $session->setLoginData($servicename, $guid, $ID); 29 $service = $this->loadService($servicename); 30 $service->initOAuthService($guid); 31 $service->login(); // redirects 32 } 33 34 /** 35 * Continues the flow from various states 36 * 37 * @return bool true if the login has been handled 38 * @throws Exception 39 * @throws \OAuth\Common\Exception\Exception 40 */ 41 public function continueFlow() 42 { 43 return $this->loginByService() or 44 $this->loginBySession() or 45 $this->loginByCookie(); 46 } 47 48 /** 49 * Second step in a explicit login, validates the oauth code 50 * 51 * @return bool true if successful, false if not applies 52 * @throws \OAuth\Common\Exception\Exception 53 * @throws Exception 54 */ 55 protected function loginByService() 56 { 57 global $INPUT; 58 59 if (!$INPUT->get->has('code') && !$INPUT->get->has('oauth_token')) { 60 return false; 61 } 62 63 $session = Session::getInstance(); 64 65 // init service from session 66 $logindata = $session->getLoginData(); 67 if (!$logindata) return false; 68 $service = $this->loadService($logindata['servicename']); 69 $service->initOAuthService($logindata['guid']); 70 $session->clearLoginData(); 71 72 // oAuth login 73 if (!$service->checkToken()) throw new Exception("Login failed"); 74 $userdata = $service->getUser(); 75 76 // processing 77 $userdata = $this->validateUserData($userdata, $logindata['servicename']); 78 $userdata = $this->processUserData($userdata, $logindata['servicename']); 79 80 // login 81 $session->setUser($userdata); // log in 82 $session->setCookie($logindata['servicename'], $logindata['guid']); // set cookie 83 84 // redirect to the appropriate ID 85 if (!empty($logindata['id'])) { 86 send_redirect(wl($logindata['id'], [], true, '&')); 87 } 88 return true; 89 } 90 91 /** 92 * Login based on user's current session data 93 * 94 * This will also log in plainauth users 95 * 96 * @return bool true if successful, false if not applies 97 * @throws Exception 98 */ 99 protected function loginBySession() 100 { 101 $session = Session::getInstance(); 102 if (!$session->isValid()) { 103 $session->clear(); 104 return false; 105 } 106 107 $userdata = $session->getUser(); 108 if(!$userdata) return false; 109 $session->setUser($userdata, false); // does a login without resetting the time 110 return true; 111 } 112 113 /** 114 * Login based on user cookie and a previously saved access token 115 * 116 * @return bool true if successful, false if not applies 117 * @throws Exception 118 */ 119 protected function loginByCookie() 120 { 121 $session = Session::getInstance(); 122 $cookie = $session->getCookie(); 123 if (!$cookie) return false; 124 125 try { 126 $service = $this->loadService($cookie['servicename']); 127 $service->initOAuthService($cookie['guid']); 128 } catch (Exception $e) { 129 return false; // maybe cookie had old service that is no longer available 130 } 131 132 // this should use a previously saved token 133 $userdata = $service->getUser(); 134 135 // processing 136 $userdata = $this->validateUserData($userdata, $cookie['servicename']); 137 $userdata = $this->processUserData($userdata, $cookie['servicename']); 138 139 $session->setUser($userdata); // log in 140 return true; 141 } 142 143 // endregion 144 145 /** 146 * Clean and validate the user data provided from the service 147 * 148 * @param array $userdata 149 * @param string $servicename 150 * @return array 151 * @throws Exception 152 * @todo test 153 */ 154 protected function validateUserData($userdata, $servicename) 155 { 156 /** @var \auth_plugin_oauth */ 157 global $auth; 158 159 // mail is required 160 if (empty($userdata['mail'])) { 161 throw new Exception("$servicename did not provide the an email address. Can't log you in"); 162 } 163 164 // mail needs to be allowed 165 /** @var \helper_plugin_oauth $hlp */ 166 $hlp = plugin_load('helper', 'oauth'); 167 $hlp->checkMail($userdata['mail']); 168 169 // make username from mail if empty 170 $userdata['user'] = $auth->cleanUser((string)$userdata['user']); 171 if ($userdata === '') { 172 list($userdata['user']) = explode('@', $userdata['mail']); 173 } 174 175 // make full name from username if empty 176 if (empty($userdata['name'])) { 177 $userdata['name'] = $userdata['user']; 178 } 179 180 return $userdata; 181 } 182 183 /** 184 * Process the userdata, update the user info array and create the user if necessary 185 * 186 * Uses the global $auth object for user management 187 * 188 * @param array $userdata User info received from authentication 189 * @param string $servicename Auth service 190 * @return array the modified user info 191 * @throws Exception 192 */ 193 protected function processUserData($userdata, $servicename) 194 { 195 /** @var \auth_plugin_oauth $auth */ 196 global $auth; 197 198 // see if the user is known already 199 $localUser = $auth->getUserByEmail($userdata['mail']); 200 if ($localUser) { 201 $localUserInfo = $auth->getUserData($localUser); 202 // check if the user allowed access via this service 203 if (!in_array($auth->cleanGroup($servicename), $localUserInfo['grps'])) { 204 throw new Exception(sprintf($auth->getLang('authnotenabled'), $servicename)); 205 } 206 $userdata['user'] = $localUser; 207 $userdata['name'] = $localUserInfo['name']; 208 $userdata['grps'] = array_merge((array)$userdata['grps'], $localUserInfo['grps']); 209 } elseif (actionOK('register') || $auth->getConf('register-on-auth')) { 210 if (!$auth->registerOAuthUser($userdata, $servicename)) { 211 throw new Exception('something went wrong creating your user account. please try again later.'); 212 } 213 } else { 214 throw new Exception($auth->getLang('addUser not possible')); 215 } 216 217 return $userdata; 218 } 219 220 /** 221 * Instantiates a Service by name 222 * 223 * @param string $servicename 224 * @return Service 225 * @throws Exception 226 */ 227 protected function loadService($servicename) 228 { 229 /** @var \helper_plugin_oauth $hlp */ 230 $hlp = plugin_load('helper', 'oauth'); 231 $srv = $hlp->loadService($servicename); 232 233 if ($srv === null) throw new Exception("No such service $servicename"); 234 return $srv; 235 } 236 237} 238