1<?php 2// must be run within Dokuwiki 3if(!defined('DOKU_INC')) die(); 4 5use dokuwiki\Extension\Event; 6 7/** 8* Chained authentication backend 9* 10* @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 11* @author Philipp Neuser <pneuser@physik.fu-berlin.de> 12* @author Christian Marg <marg@rz.tu-clausthal.de> 13* 14* Based on "Chained authentication backend" 15* by Grant Gardner <grant@lastweekend.com.au> 16* see https://www.dokuwiki.org/auth:ggauth 17* 18*/ 19class auth_plugin_authchained extends DokuWiki_Auth_Plugin { 20 public $success = true; 21 //array with authentication plugins 22 protected $chained_plugins = array(); 23 protected $chained_auth = NULL; 24 protected $usermanager_auth = NULL; 25 protected $any_external = false; 26 27 /** 28 * Constructor. 29 * 30 * Loads all configured plugins or the authentication plugin of the 31 * logged in user. 32 * 33 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 34 * @author Christian Marg <marg@rz.tu-clausthal.de> 35 */ 36 public function __construct() { 37 global $conf; 38 // call parent 39 # parent::__constructor(); 40 41 //check if there is already an authentication plugin selected 42 if( isset($_SESSION[DOKU_COOKIE]['plugin']['authchained']['module']) && 43 !empty($_SESSION[DOKU_COOKIE]['plugin']['authchained']['module']) ) { 44 45 //get previously selected authentication plugin 46 $this->chained_auth =& plugin_load('auth',$_SESSION[DOKU_COOKIE]['plugin']['authchained']['module']); 47 if ( is_null($this->chained_auth) || !$this->chained_auth->success ) { 48 $this->success = false; 49 } 50 } 51 52 //get authentication plugins 53 if($this->getConf('authtypes')){ 54 foreach(explode(":",$this->getConf('authtypes')) as $tmp_plugin){ 55 $tmp_class =& plugin_load('auth',$tmp_plugin); 56 57 if ( !is_null($tmp_class) || $tmp_class->success ) { 58 $tmp_module = array($tmp_plugin,$tmp_class); 59 array_push($this->chained_plugins, $tmp_module); 60 $this->any_external |= $tmp_class->canDo('external'); 61 } else { 62 msg("Problem constructing $tmp_plugin",-1); 63 $this->success = false; 64 } 65 } 66 } else { 67 $success = false; 68 } 69 70 // If defined, instantiate usermanager authtype. 71 // No need to check for duplicates, "plugin_load" does that for us. 72 if($this->getConf('usermanager_authtype')){ 73 $this->usermanager_auth =& plugin_load('auth',$this->getConf('usermanager_authtype')); 74 if(is_null($this->usermanager_auth) || !$this->usermanager_auth->success ) { 75 msg("Problem constructing usermanager authtype: ".$this->getConf('usermanager_authtype'),-1); 76 $this->success = false; 77 } 78 } else { 79 $this->usermanager_auth =& $this->chained_auth; 80 } 81 82 //debug 83 // print_r($chained_plugins); 84 } 85 86 /** 87 * Forwards the authentication to configured authplugins. 88 * Returns true, if the usermanager authtype has the capability and no user 89 * is logged in. 90 * 91 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 92 * @author Christian Marg <marg@rz.tu-clausthal.de> 93 * @param string $cap the capability to check 94 * @return bool 95 */ 96 public function canDo($cap) { 97 global $ACT; 98 # print_r($cap); 99 if(is_null($this->chained_auth)) { 100 if ($cap == "external") { 101 return $this->any_external; 102 } 103 if (!is_null($this->usermanager_auth)) { 104 return $this->usermanager_auth->canDo($cap); 105 } else { 106 return parent::canDo($cap); 107 } 108 } else { 109 switch($cap) { 110 case 'Profile': 111 case 'logout': 112 case 'external': 113 //Depends on current user. 114 return $this->chained_auth->canDo($cap); 115 case 'UserMod': 116 case 'addUser': 117 case 'delUser': 118 case 'getUsers': 119 case 'getUserCount': 120 case 'getGroups': 121 //Depends on the auth for use with user manager 122 return $this->usermanager_auth->canDo($cap); 123 case 'modPass': 124 case 'modName': 125 case 'modLogin': 126 case 'modGroups': 127 case 'modMail': 128 /** 129 * Use request attributes to guess whether we are in the Profile or UserManager 130 * and return the appropriate auth capabilities 131 */ 132 if ($ACT == "admin" && isset($_REQUEST['page']) && $_REQUEST['page']=="usermanager") { 133 return $this->usermanager_auth->canDo($cap); 134 } else { 135 // assume we want profile info. 136 return $this->chained_auth->canDo($cap); 137 } 138 default: 139 //Everything else (false) 140 return parent::canDo($cap); 141 } 142 #echo "canDo $cap ".$this->chained_auth->canDo($cap)."\n"; 143 } 144 } 145 146 /** 147 * Forwards the result of the auth plugin of the logged in user and 148 * unsets our session variable. 149 * @see auth_logoff() 150 * @author Philipp Neuser <pneuser@physik.fu-berlin.de 151 * @author Christian Marg <marg@rz.tu-clausthal.de> 152 */ 153 public function logOff() { 154 if(!is_null($this->chained_auth)) 155 $this->chained_auth->logOff(); 156 unset($_SESSION[DOKU_COOKIE]['plugin']['authchained']['module']); 157 } 158 159 /** 160 * Do all authentication [ OPTIONAL ] 161 * If the current plugin is external, be external. 162 * 163 * @see auth_login() 164 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 165 * @author Christian Marg <marg@rz.tu-clausthal.de> 166 * 167 * @param string $user Username 168 * @param string $pass Cleartext Password 169 * @param bool $sticky Cookie should not expire 170 * @return bool true on successful auth 171 */ 172 public function trustExternal($user, $pass, $sticky = false) { 173 global $INPUT; 174 foreach($this->chained_plugins as $module) { 175 if($module[1]->canDo('external') && $module[1]->trustExternal($user, $pass, $sticky)) { 176 $_SESSION[DOKU_COOKIE]['plugin']['authchained']['module'] = $module[0]; 177 $this->chained_auth = $module[1]; 178 return true; 179 } 180 } 181 $evdata = array( 182 'user' => $INPUT->str('u'), 183 'password' => $INPUT->str('p'), 184 'sticky' => $INPUT->bool('r'), 185 'silent' => $INPUT->bool('http_credentials') 186 ); 187 Event::createAndTrigger('AUTH_LOGIN_CHECK', $evdata, 'auth_login_wrapper'); 188 return false; 189 } 190 191 /** 192 * Check user+password [ MUST BE OVERRIDDEN ] 193 * 194 * Checks if the given user exists in one of the plugins and checks 195 * against the given password. The first plugin returning true becomes 196 * auth plugin of the user session. 197 * 198 * @author Philipp Neuser <pneuser@physik.fu-berlin.de 199 * @author Christian Marg <marg@rz.tu-clausthal.de> 200 * @param string $user the user name 201 * @param string $pass the clear text password 202 * @return bool 203 */ 204 public function checkPass($user, $pass) { 205 //debug 206 // print_r($this->chained_plugins); 207 if(!is_null($this->chained_auth)) 208 return $this->chained_auth->checkPass($user, $pass); 209 foreach($this->chained_plugins as $module) { 210 if($module[1]->canDo('external') && $module[1]->trustExternal($user, $pass)) { 211 $_SESSION[DOKU_COOKIE]['plugin']['authchained']['module'] = $module[0]; 212 $this->chained_auth = $module[1]; 213 return true; 214 } 215 if($module[1]->checkPass($user, $pass)) { 216 $_SESSION[DOKU_COOKIE]['plugin']['authchained']['module'] = $module[0]; 217 $this->chained_auth = $module[1]; 218 return true; 219 } 220 } 221 return false; 222 } 223 224 /** 225 * Forwards the result of the auth plugin of the logged in user or 226 * checks all plugins if the users exists. The first plugin returning 227 * data is used. 228 * 229 * name string full name of the user 230 * mail string email addres of the user 231 * grps array list of groups the user is in 232 * 233 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 234 * @author Christian Marg <marg@rz.tu-clausthal.de> 235 * @param string $user the user name 236 * @return array containing user data or false 237 */ 238 public function getUserData($user, $requireGroups=true) { 239 global $ACT, $INPUT; 240 241 //if(!$this->cando['external']) msg("no valid authorisation system in use", -1); 242 // echo "TESTSETEST"; 243 244 //print_r($this->chained_auth); 245 if ($ACT == "admin" && isset($_REQUEST['page']) && $_REQUEST['page']=="usermanager") { 246 if(!is_null($this->usermanager_auth)) 247 return $this->usermanager_auth->getUserData($user); 248 } 249 250 if(is_null($this->chained_auth)||(!is_null($INPUT->server) && $user != $INPUT->server->str('REMOTE_USER'))) { 251 foreach($this->chained_plugins as $module) { 252 $tmp_array = $module[1]->getUserData($user); 253 if(!is_bool($tmp_array)) 254 $tmp_chk_arr =array_filter($tmp_array); 255 if(!empty($tmp_chk_arr) && $tmp_array) 256 return $tmp_array; 257 } 258 return false; 259 } else { 260 return $this->chained_auth->getUserData($user); 261 } 262 } 263 264 /** 265 * Forwards the result of the auth plugin of the logged in user or 266 * returns null. 267 * 268 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 269 * @author Christian Marg <marg@rz.tu-clausthal.de> 270 * @param string $user 271 * @param string $pass 272 * @param string $name 273 * @param string $mail 274 * @param null|array $grps 275 * @return bool|null 276 */ 277 public function createUser($user, $pass, $name, $mail, $grps = null) { 278 if(!is_null($this->usermanager_auth) && $this->canDo('addUser')) { 279 return $this->usermanager_auth->createUser($user, $pass, $name, $mail, $grps); 280 } else { 281 msg("authorisation method does not allow creation of new users", -1); 282 return null; 283 } 284 } 285 286 /** 287 * Forwards the result of the auth plugin of the logged in user or 288 * returns false 289 * 290 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 291 * @author Christian Marg <marg@rz.tu-clausthal.de> 292 * @param string $user nick of the user to be changed 293 * @param array $changes array of field/value pairs to be changed (password will be clear text) 294 * @return bool 295 */ 296 public function modifyUser($user, $changes) { 297 if(!is_null($this->usermanager_auth) && $this->canDo('UserMod') ) { 298 return $this->usermanager_auth->modifyUser($user, $changes); 299 } else { 300 msg("authorisation method does not allow modifying of user data", -1); 301 return null; 302 } 303 } 304 305 /** 306 * Forwards the result of the auth plugin of the logged in user or 307 * returns false 308 * 309 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 310 * @author Christian Marg <marg@rz.tu-clausthal.de> 311 * @param array $users 312 * @return int number of users deleted 313 */ 314 public function deleteUsers($users) { 315 if(!is_null($this->usermanager_auth) && $this->canDo('delUser') ) { 316 return $this->usermanager_auth->deleteUsers($users); 317 }else{ 318 msg("authorisation method does not allow deleting of users", -1); 319 return false; 320 } 321 } 322 323 /** 324 * Forwards the result of the auth plugin of the logged in user or 325 * returns 0 326 * 327 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 328 * @author Christian Marg <marg@rz.tu-clausthal.de> 329 * @param array $filter array of field/pattern pairs, empty array for no filter 330 * @return int 331 */ 332 public function getUserCount($filter = array()) { 333 if(!is_null($this->usermanager_auth) && $this->canDo('getUserCount') ){ 334 return $this->usermanager_auth->getUserCount($filter); 335 } else { 336 msg("authorisation method does not provide user counts", -1); 337 return 0; 338 } 339 } 340 341 /** 342 * Forwards the result of the auth plugin of the logged in user or 343 * returns empty array 344 * 345 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 346 * @author Christian Marg <marg@rz.tu-clausthal.de> 347 * @param int $start index of first user to be returned 348 * @param int $limit max number of users to be returned 349 * @param array $filter array of field/pattern pairs, null for no filter 350 * @return array list of userinfo (refer getUserData for internal userinfo details) 351 */ 352 public function retrieveUsers($start = 0, $limit = -1, $filter = null) { 353 if(!is_null($this->usermanager_auth) && $this->canDo('getUsers') ) { 354 //msg("RetrieveUsers is using ".get_class($this->usermanager_auth)); 355 return $this->usermanager_auth->retrieveUsers($start, $limit, $filter); 356 } else { 357 msg("authorisation method does not support mass retrievals", -1); 358 return array(); 359 } 360 } 361 362 /** 363 * Forwards the result of the auth plugin of the logged in user or 364 * returns false 365 * 366 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 367 * @author Christian Marg <marg@rz.tu-clausthal.de> 368 * @param string $group 369 * @return bool 370 */ 371 public function addGroup($group) { 372 if(!is_null($this->usermanager_auth) && $this->canDo('addGroup') ) { 373 return $this->usermanager_auth->addGroup($group); 374 } else { 375 msg("authorisation method does not support independent group creation", -1); 376 return false; 377 } 378 } 379 380 /** 381 * Forwards the result of the auth plugin of the logged in user or 382 * returns empty array 383 * 384 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 385 * @author Christian Marg <marg@rz.tu-clausthal.de> 386 * @param int $start 387 * @param int $limit 388 * @return array 389 */ 390 public function retrieveGroups($start = 0, $limit = 0) { 391 if(!is_null($this->usermanager_auth) && $this->canDo('getGroups') ) { 392 return $this->usermanager_auth->retrieveGroups($start,$limit); 393 } else { 394 msg("authorisation method does not support group list retrieval", -1); 395 return array(); 396 } 397 } 398 399 /** 400 * Forwards the result of the auth plugin of the logged in user or 401 * returns true 402 * 403 * @return bool 404 */ 405 public function isCaseSensitive() { 406 if(is_null($this->chained_auth)) 407 return parent::isCaseSensitive(); 408 else 409 return $this->chained_auth->isCaseSensitive(); 410 } 411 412 /** 413 * Sanitize a given username [OPTIONAL] 414 * Forwards the result of the auth plugin of the logged in user or 415 * returns false 416 * 417 * 418 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 419 * @author Christian Marg <marg@rz.tu-clausthal.de> 420 * @param string $user username 421 * @return string the cleaned username 422 */ 423 public function cleanUser($user) { 424 global $ACT; 425 //print_r($this->chained_auth); 426 if ($ACT == "admin" && isset($_REQUEST['page']) && $_REQUEST['page']=="usermanager") { 427 if(!is_null($this->usermanager_auth)) 428 return $this->usermanager_auth->cleanUser($user); 429 } else { 430 if(!is_null($this->chained_auth)) 431 return $this->chained_auth->cleanUser($user); 432 } 433 return parent::cleanUser($user); 434 } 435 436 /** 437 * Sanitize a given groupname [OPTIONAL] 438 * Forwards the result of the auth plugin of the logged in user or 439 * returns false 440 * 441 * @author Philipp Neuser <pneuser@physik.fu-berlin.de> 442 * @author Christian Marg <marg@rz.tu-clausthal.de> 443 * @param string $group groupname 444 * @return string the cleaned groupname 445 */ 446 public function cleanGroup($group) { 447 global $ACT; 448 if ($ACT == "admin" && isset($_REQUEST['page']) && $_REQUEST['page']=="usermanager") { 449 if(!is_null($this->usermanager_auth)) 450 return $this->usermanager_auth->cleanGroup($group); 451 } else { 452 if(!is_null($this->chained_auth)) 453 return $this->chained_auth->cleanGroup($group); 454 } 455 return parent::cleanGroup($group); 456 } 457 458 459 public function useSessionCache($user) { 460 global $conf; 461 if(is_null($this->chained_auth)) 462 return parent::useSessionCache($user); 463 else 464 return $this->chained_auth->useSessionCache($user); 465 } 466 467} 468