1<?php 2/** 3 * DokuWiki Plugin oauth (Action Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9// must be run within Dokuwiki 10if(!defined('DOKU_INC')) die(); 11 12class action_plugin_evesso extends DokuWiki_Action_Plugin { 13 14 /** 15 * Registers a callback function for a given event 16 * 17 * @param Doku_Event_Handler $controller DokuWiki's event controller object 18 * @return void 19 */ 20 public function register(Doku_Event_Handler $controller) { 21 global $conf; 22 if($conf['authtype'] != 'evesso') return; 23 24 $conf['profileconfirm'] = false; // password confirmation doesn't work with oauth only users 25 26 $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'handle_start'); 27 $controller->register_hook('HTML_LOGINFORM_OUTPUT', 'BEFORE', $this, 'handle_loginform'); 28 $controller->register_hook('HTML_UPDATEPROFILEFORM_OUTPUT', 'BEFORE', $this, 'handle_profileform'); 29 $controller->register_hook('FORM_LOGIN_OUTPUT', 'BEFORE', $this, 'handle_loginform'); 30 $controller->register_hook('FORM_UPDATEPROFILE_OUTPUT', 'BEFORE', $this, 'handle_profileform'); 31 $controller->register_hook('AUTH_USER_CHANGE', 'BEFORE', $this, 'handle_usermod'); 32 $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_dologin'); 33 } 34 35 /** 36 * Start an oAuth login or restore environment after successful login 37 * 38 * @param Doku_Event $event event object by reference 39 * @param mixed $param [the parameters passed as fifth argument to register_hook() when this 40 * handler was registered] 41 * @return void 42 */ 43 public function handle_start(Doku_Event &$event, $param) { 44 global $ID; 45 if (isset($_SESSION[DOKU_COOKIE]['oauth-logout'])){ 46 unset($_SESSION[DOKU_COOKIE]['oauth-logout']); 47 send_redirect(wl($ID)); 48 return; 49 } 50 if (isset($_SESSION[DOKU_COOKIE]['oauth-done']['do']) || !empty($_SESSION[DOKU_COOKIE]['oauth-done']['rev'])){ 51 $this->restoreSessionEnvironment(); 52 return; 53 } 54 $this->updateGroups(); 55 $this->startOAuthLogin(); 56 } 57 58 public function updateGroups() { 59 global $AUTH_ACL, $ID; 60 $showMessage = false; 61 $updateACL = false; 62 foreach ($AUTH_ACL as $index => $value) { //Search 63 $acl = preg_split("/\s/", $value); 64 if ($this->startsWith($acl[1], "@eve%2d")) { 65 $showMessage = true; 66 if ($acl[2] != 0) { 67 $updateACL = true; 68 } 69 } 70 } 71 if ($updateACL) { 72 $apa = plugin_load('admin', 'acl'); 73 foreach ($AUTH_ACL as $index => $value) { 74 $acl = preg_split("/\s/", $value); 75 if ($this->startsWith($acl[1], "@eve%2d") && $acl[2] != 0) { 76 if (method_exists($apa, "addOrUpdateACL")) { 77 $apa->addOrUpdateACL($acl[0], rawurldecode($acl[1]), 0); //Scope, User/Group 78 } else { //Greebo and bellow 79 $apa->_acl_del($acl[0], rawurldecode($acl[1])); // first make sure we won't end up with 2 lines matching this user and scope. 80 $apa->_acl_add($acl[0], rawurldecode($acl[1]), 0); //Scope, User/Group 81 } 82 $AUTH_ACL[$index] = $acl[0] . ' ' . $acl[1] . ' ' . '0'; 83 } 84 } 85 } 86 if ($showMessage) { 87 msg('<b>EVESSO:</b><br/>' 88 . 'The naming of eve groups was changed in this version of EVESSO.<br />' 89 . 'Access for all the deprecated groups have been set to <code>None</code>.<br />' 90 . 'See the <a href="https://github.com/GoldenGnu/dokuwiki-plugin-evesso#updating">readme</a> for the details on how the naming have changed.<br />' 91 . 'Update your <a href="' . wl($ID, array('do' => 'admin', 'page' => 'acl'), true, '&') . '">ACL</a> settings to restore access.<br />' 92 . 'This message will remain until the deprecated groups are removed from ACL.<br />' 93 , 2, '', '', MSG_ADMINS_ONLY); 94 } 95 } 96 97 private function startOAuthLogin() { 98 global $INPUT, $ID; 99 100 /** @var helper_plugin_evesso $hlp */ 101 $hlp = plugin_load('helper', 'evesso'); 102 $servicename = $INPUT->str('evessologin'); 103 $service = $hlp->loadService($servicename); 104 if(is_null($service)) return; 105 106 // remember service in session 107 session_start(); 108 $_SESSION[DOKU_COOKIE]['oauth-inprogress']['service'] = $servicename; 109 $_SESSION[DOKU_COOKIE]['oauth-inprogress']['id'] = $ID; 110 session_write_close(); 111 112 $service->login(); 113 } 114 115 private function restoreSessionEnvironment() { 116 global $INPUT, $ACT, $TEXT, $PRE, $SUF, $SUM, $RANGE, $DATE_AT, $REV; 117 $ACT = $_SESSION[DOKU_COOKIE]['oauth-done']['do']; 118 $_REQUEST = $_SESSION[DOKU_COOKIE]['oauth-done']['$_REQUEST']; 119 120 $REV = $INPUT->int('rev'); 121 $DATE_AT = $INPUT->str('at'); 122 $RANGE = $INPUT->str('range'); 123 if($INPUT->post->has('wikitext')) { 124 $TEXT = cleanText($INPUT->post->str('wikitext')); 125 } 126 $PRE = cleanText(substr($INPUT->post->str('prefix'), 0, -1)); 127 $SUF = cleanText($INPUT->post->str('suffix')); 128 $SUM = $INPUT->post->str('summary'); 129 130 unset($_SESSION[DOKU_COOKIE]['oauth-done']); 131 } 132 133 /** 134 * Save groups for all the services a user has enabled 135 * 136 * @param Doku_Event $event event object by reference 137 * @param mixed $param [the parameters passed as fifth argument to register_hook() when this 138 * handler was registered] 139 * @return void 140 */ 141 public function handle_usermod(Doku_Event &$event, $param) { 142 global $ACT; 143 global $USERINFO; 144 global $auth; 145 global $INPUT; 146 147 if($event->data['type'] != 'modify') return; 148 if($ACT != 'profile') return; 149 150 // we want to modify the user's groups 151 $groups = $USERINFO['grps']; //current groups 152 if(isset($event->data['params'][1]['grps'])) { 153 // something already defined new groups 154 $groups = $event->data['params'][1]['grps']; 155 } 156 157 /** @var helper_plugin_evesso $hlp */ 158 $hlp = plugin_load('helper', 'evesso'); 159 160 // get enabled and configured services 161 $enabled = $INPUT->arr('oauth_group'); 162 163 // add all enabled services as group, remove all disabled services 164 if(isset($enabled['EveOnline'])) { //Add EveOnline 165 $groups[] = 'eveonline'; 166 } else { //Remove EveOnline 167 $idx = array_search('eveonline', $groups); 168 if($idx !== false) unset($groups[$idx]); 169 } 170 $groups = array_unique($groups); 171 172 // add new group array to event data 173 $event->data['params'][1]['grps'] = $groups; 174 175 } 176 177 /** 178 * Add service selection to user profile 179 * 180 * @param Doku_Event $event event object by reference 181 * @param mixed $param [the parameters passed as fifth argument to register_hook() when this 182 * handler was registered] 183 * @return void 184 */ 185 public function handle_profileform(Doku_Event &$event, $param) { 186 global $USERINFO; 187 /** @var auth_plugin_authplain $auth */ 188 global $auth; 189 190 /** @var helper_plugin_evesso $hlp */ 191 $hlp = plugin_load('helper', 'evesso'); 192 193 /** @var Doku_Form $form */ 194 $form =& $event->data; 195 if (!in_array('eveonline', $USERINFO['grps'])) { 196 return; //Continue as normal 197 } 198 if(is_a($form, \dokuwiki\Form\Form::class)) { //Igor and later 199 //Disable fullname field 200 $pos = $form->findPositionByAttribute('name', 'fullname'); 201 $form->getElementAt($pos)->attr('disabled', 'disabled'); 202 //Remove all fields except username and fullname 203 $start = $form->findPositionByAttribute('name', 'fullname') + 1; 204 $done = 0; 205 while ($form->elementCount() > $start && $done < 11) { 206 $form->removeElement($start); 207 $done++; 208 } 209 $pos = $form->elementCount(); 210 //Add corporation, alliance, faction fields 211 foreach ($USERINFO['grps'] as $group) { 212 if ($this->startsWith($group, helper_plugin_evesso::CORPORATION_PREFIX)) { 213 $corp = $this->replaceFirst($group, helper_plugin_evesso::CORPORATION_PREFIX); 214 } 215 if ($this->startsWith($group, helper_plugin_evesso::ALLIANCE_PREFIX)) { 216 $alliance = $this->replaceFirst($group, helper_plugin_evesso::ALLIANCE_PREFIX); 217 } 218 if ($this->startsWith($group, helper_plugin_evesso::FACTION_PREFIX)) { 219 $faction = $this->replaceFirst($group, helper_plugin_evesso::FACTION_PREFIX); 220 } 221 } 222 if (isset($faction)) { //str_starts_with 223 $this->insertTextInput($form, $pos, $faction, $this->getLang('faction')); 224 } 225 if (isset($alliance)) { //str_starts_with 226 $this->insertTextInput($form, $pos, $alliance, $this->getLang('alliance')); 227 } 228 if (isset($corp)) { //str_starts_with 229 $this->insertTextInput($form, $pos, $corp, $this->getLang('corporation')); 230 } 231 } else { //Hogfather and earlier 232 //Remove all fields except username and fullname 233 array_splice($form->_content, 3); 234 //Disable fullname field 235 $form->getElementAt(3)['disabled'] = 'disabled'; 236 //Add corporation, alliance, faction fields 237 $pos = count($form->_content); 238 foreach ($USERINFO['grps'] as $group) { 239 if ($this->startsWith($group, helper_plugin_evesso::CORPORATION_PREFIX)) { 240 $corp = $this->replaceFirst($group, helper_plugin_evesso::CORPORATION_PREFIX); 241 } 242 if ($this->startsWith($group, helper_plugin_evesso::ALLIANCE_PREFIX)) { 243 $alliance = $this->replaceFirst($group, helper_plugin_evesso::ALLIANCE_PREFIX); 244 } 245 if ($this->startsWith($group, helper_plugin_evesso::FACTION_PREFIX)) { 246 $faction = $this->replaceFirst($group, helper_plugin_evesso::FACTION_PREFIX); 247 } 248 } 249 $form->insertElement($pos, form_closefieldset()); 250 if (isset($faction)) { //str_starts_with 251 $form->insertElement($pos, form_makeTextField($faction, $faction, $this->getLang('faction'), '', 'block', array('disabled' => 'disabled', 'size' => '50'))); 252 } 253 if (isset($alliance)) { //str_starts_with 254 $form->insertElement($pos, form_makeTextField($alliance, $alliance, $this->getLang('alliance'), '', 'block', array('disabled' => 'disabled', 'size' => '50'))); 255 } 256 if (isset($corp)) { //str_starts_with 257 $form->insertElement($pos, form_makeTextField($corp, $corp, $this->getLang('corporation'), '', 'block', array('disabled' => 'disabled', 'size' => '50'))); 258 } 259 } 260 } 261 262 private function startsWith($haystack, $needle) { 263 $length = strlen($needle); 264 return (substr($haystack, 0, $length) === $needle); 265 } 266 267 private function replaceFirst($haystack, $needle, $replace = '') { 268 $pos = strpos($haystack, $needle); 269 if ($pos !== false) { 270 $haystack = substr_replace($haystack, $replace, $pos, strlen($needle)); 271 } 272 return $haystack; 273 } 274 275 private function insertTextInput($form, $pos, $value, $name) { 276 $textInput = $form->addTextInput($value, $name, $pos); 277 $textInput->attr('size', '50'); 278 $textInput->attr('class', 'edit'); 279 $textInput->attr('value', $value); 280 $textInput->attr('disabled', 'disabled'); 281 $label = $textInput->getLabel(); 282 $label->attr('class', 'block'); 283 $form->addHTML('<br>', $pos); 284 } 285 286 private function insertElement($form, $pos, $out) { 287 if(is_a($form, \dokuwiki\Form\Form::class)) { //Igor and later 288 $form->addHtml($out, $pos); 289 } else { //Hogfather and earlier 290 $form->insertElement($pos, $out); 291 } 292 } 293 294 /** 295 * Add the oAuth login links 296 * 297 * @param Doku_Event $event event object by reference 298 * @param mixed $param [the parameters passed as fifth argument to register_hook() when this 299 * handler was registered] 300 * @return void 301 */ 302 public function handle_loginform(Doku_Event &$event, $param) { 303 /** @var helper_plugin_evesso $hlp */ 304 $hlp = plugin_load('helper', 'evesso'); 305 $service = $hlp->getService(); 306 307 /** @var Doku_Form $form */ 308 $form =& $event->data; 309 $html = ''; 310 311 if ($hlp->isEveAuth()) { //Set Html 312 if(is_a($form, \dokuwiki\Form\Form::class)) { //Igor and later 313 while ($form->elementCount() > 0) { 314 $form->removeElement(0); 315 } 316 $pos = $form->elementCount() - 3; //At the end 317 } else { //Hogfather and earlier 318 $form->_content = array(); 319 $pos = 0; 320 } 321 $html = $this->service_html($service); 322 }else{ //PlainAuth and EveAuth 323 $html .= $this->service_html($service); 324 if(is_a($form, \dokuwiki\Form\Form::class)) { //Igor and later 325 $pos = $form->elementCount(); //At the end 326 } else { //Hogfather and earlier 327 $pos = count($form->_content); 328 } 329 } 330 if(is_a($form, \dokuwiki\Form\Form::class)) { //Igor and later 331 $form->addFieldsetOpen($this->getLang('loginwith'), ++$pos); 332 $form->addHtml($html, ++$pos); 333 $form->addFieldsetClose(); 334 } else { //Hogfather and earlier 335 $form->insertElement(++$pos, form_openfieldset(array('_legend' => $this->getLang('loginwith'), 'class' => 'plugin_evesso'))); 336 $form->insertElement(++$pos, $html); 337 $form->insertElement(++$pos, form_closefieldset()); 338 } 339 } 340 341 function service_html($service) { 342 global $ID; 343 $html = ''; 344 $html .= '<a href="' . wl($ID, array('evessologin' => $service)) . '" class="plugin_evesso_' . $service . '">'; 345 if ($this->getConf('login-button') == 'Button') { 346 $html .= '<div class="eve-sso-login-white-large"></div>'; 347 } elseif ($this->getConf('login-button') == 'LargeLight') { 348 $html .= '<div class="eve-sso-login-white-large"></div>'; 349 } elseif ($this->getConf('login-button') == 'LargeDark') { 350 $html .= '<div class="eve-sso-login-black-large"></div>'; 351 } elseif ($this->getConf('login-button') == 'SmallLight') { 352 $html .= '<div class="eve-sso-login-white-small"></div>'; 353 } elseif ($this->getConf('login-button') == 'SmallDark') { 354 $html .= '<div class="eve-sso-login-black-small"></div>'; 355 } else { 356 $html .= $this->getLang('loginButton'); 357 } 358 $html .= '</a> '; 359 return $html; 360 } 361 362 public function handle_dologin(Doku_Event &$event, $param) { 363 global $lang; 364 global $ID; 365 366 $hlp = plugin_load('helper', 'evesso'); 367 368 if($event->data == 'logout' && $hlp->isEveAuth()) { 369 session_start(); 370 $_SESSION[DOKU_COOKIE]['oauth-logout'] = 'logout'; 371 session_write_close(); 372 } 373 374 if ($hlp->isAuthPlain()) return true; 375 376 if($event->data != 'login') return true; 377 378 if($hlp->isEveAuthDirect()) { 379 $lang['btn_login'] = $this->getLang('loginButton'); 380 $url = wl($ID, array('evessologin' => $hlp->getService()), true, '&'); 381 send_redirect($url); 382 } 383 } 384 385} 386// vim:ts=4:sw=4:et: