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