1<?php 2 3use dokuwiki\plugin\oauth\OAuthManager; 4use dokuwiki\plugin\oauth\Session; 5use dokuwiki\Subscriptions\RegistrationSubscriptionSender; 6use OAuth\Common\Exception\Exception as OAuthException; 7 8/** 9 * DokuWiki Plugin oauth (Auth Component) 10 * 11 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 12 * @author Andreas Gohr <andi@splitbrain.org> 13 */ 14class auth_plugin_oauth extends auth_plugin_authplain 15{ 16 /** @var helper_plugin_oauth */ 17 protected $hlp; 18 19 /** @var OAuthManager */ 20 protected $om; 21 22 // region standard auth methods 23 24 /** @inheritDoc */ 25 public function __construct() 26 { 27 parent::__construct(); 28 $this->cando['external'] = true; 29 $this->hlp = $this->loadHelper('oauth'); 30 } 31 32 /** @inheritDoc */ 33 public function trustExternal($user, $pass, $sticky = false) 34 { 35 global $INPUT; 36 37 // handle redirects from farmer to animal wiki instances 38 if ($INPUT->has('state') && plugin_load('helper', 'farmer')) { 39 $this->handleFarmState($INPUT->str('state')); 40 } 41 42 try { 43 // either oauth or "normal" plain auth login via form 44 $this->om = new OAuthManager(); 45 if ($this->om->continueFlow()) return true; 46 return null; // triggers the normal auth_login() 47 } catch (OAuthException $e) { 48 $this->hlp->showException($e); 49 auth_logoff(); // clears all session and cookie data 50 return false; 51 } 52 } 53 54 /** 55 * Enhance function to check against duplicate emails 56 * 57 * @inheritdoc 58 */ 59 public function createUser($user, $pwd, $name, $mail, $grps = null) 60 { 61 if ($this->getUserByEmail($mail)) { 62 msg($this->getLang('emailduplicate'), -1); 63 return false; 64 } 65 66 return parent::createUser($user, $pwd, $name, $mail, $grps); 67 } 68 69 /** 70 * Enhance function to check against duplicate emails 71 * 72 * @inheritdoc 73 */ 74 public function modifyUser($user, $changes) 75 { 76 global $conf; 77 78 if (isset($changes['mail'])) { 79 $found = $this->getUserByEmail($changes['mail']); 80 if ($found && $found != $user) { 81 msg($this->getLang('emailduplicate'), -1); 82 return false; 83 } 84 } 85 86 $ok = parent::modifyUser($user, $changes); 87 88 // refresh session cache 89 touch($conf['cachedir'] . '/sessionpurge'); 90 return $ok; 91 } 92 93 /** 94 * Unset additional stuff in session on logout 95 */ 96 public function logOff() 97 { 98 parent::logOff(); 99 if (isset($this->om)) { 100 $this->om->logout(); 101 } 102 (Session::getInstance())->clear(); 103 } 104 105 // endregion 106 107 /** 108 * Register a new user logged in by oauth 109 * 110 * It ensures the username is unique, by adding a number if needed. 111 * Default and service name groups are set here. 112 * Registration notifications are triggered. 113 * 114 * @param array $userinfo This will be updated with the new username 115 * @param string $servicename 116 * 117 * @return bool 118 * @todo - should this be part of the OAuthManager class instead? 119 */ 120 public function registerOAuthUser(&$userinfo, $servicename) 121 { 122 global $conf; 123 $user = $userinfo['user']; 124 $count = ''; 125 while ($this->getUserData($user . $count)) { 126 if ($count) { 127 $count++; 128 } else { 129 $count = 1; 130 } 131 } 132 $user = $user . $count; 133 $userinfo['user'] = $user; 134 $groups_on_creation = []; 135 $groups_on_creation[] = $conf['defaultgroup']; 136 $groups_on_creation[] = $this->cleanGroup($servicename); // add service as group 137 $userinfo['grps'] = array_merge((array)$userinfo['grps'], $groups_on_creation); 138 139 // the password set here will remain unknown to the user 140 $ok = $this->triggerUserMod( 141 'create', 142 [ 143 $user, 144 auth_pwgen($user), 145 $userinfo['name'], 146 $userinfo['mail'], 147 $userinfo['grps'], 148 ] 149 ); 150 if (!$ok) { 151 return false; 152 } 153 154 // send notification about the new user 155 $subscriptionSender = new RegistrationSubscriptionSender(); 156 $subscriptionSender->sendRegister($user, $userinfo['name'], $userinfo['mail']); 157 158 return true; 159 } 160 161 /** 162 * Find a user by email address 163 * 164 * @param $mail 165 * @return bool|string 166 */ 167 public function getUserByEmail($mail) 168 { 169 if ($this->users === null) { 170 $this->loadUserData(); 171 } 172 $mail = strtolower($mail); 173 174 foreach ($this->users as $user => $userinfo) { 175 if (strtolower($userinfo['mail']) == $mail) return $user; 176 } 177 178 return false; 179 } 180 181 /** 182 * Fall back to plain auth strings 183 * 184 * @inheritdoc 185 */ 186 public function getLang($id) 187 { 188 $result = parent::getLang($id); 189 if ($result) return $result; 190 191 $parent = new auth_plugin_authplain(); 192 return $parent->getLang($id); 193 } 194 195 /** 196 * Farmer plugin support 197 * 198 * When coming back to farmer instance via OAUTH redirectURI, we need to redirect again 199 * to a proper animal instance detected from $state 200 * 201 * @param $state 202 */ 203 protected function handleFarmState($state) 204 { 205 /** @var \helper_plugin_farmer $farmer */ 206 $farmer = plugin_load('helper', 'farmer', false, true); 207 $data = json_decode(base64_decode(urldecode($state))); 208 if (empty($data->animal) || $farmer->getAnimal() == $data->animal) { 209 return; 210 } 211 $animal = $data->animal; 212 $allAnimals = $farmer->getAllAnimals(); 213 if (!in_array($animal, $allAnimals)) { 214 msg('Animal ' . $animal . ' does not exist!'); 215 return; 216 } 217 global $INPUT; 218 $url = $farmer->getAnimalURL($animal) . '/doku.php?' . $INPUT->server->str('QUERY_STRING'); 219 send_redirect($url); 220 } 221} 222