1<?php 2/** 3 * DokuWiki Plugin authucenter (Auth Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author daxingplay <daxingplay@gmail.com> 7 */ 8 9// must be run within Dokuwiki 10if(!defined('DOKU_INC')) die(); 11if(!defined('DOKU_PLUGIN_AUTHUCENTER_INC')) define('DOKU_PLUGIN_AUTHUCENTER_INC', dirname(__FILE__).'/'); 12 13class auth_plugin_authucenter extends DokuWiki_Auth_Plugin { 14 15 16 /** 17 * Constructor. 18 */ 19 public function __construct() { 20 parent::__construct(); // for compatibility 21 22 if (!is_dir(DOKU_INC . 'uc_client')) { 23 $this->_recurse_copy(DOKU_PLUGIN_AUTHUCENTER_INC . 'lib/uc_client', DOKU_INC); 24 } 25 26 if (!file_exists(DOKU_INC . 'api/uc.php')) { 27 $this->_recurse_copy(DOKU_PLUGIN_AUTHUCENTER_INC . 'lib/api', DOKU_INC); 28 } 29 30 if (!file_exists(DOKU_INC . 'conf/uc.auth.php') || $this->getConf('regenerateconfig')) { 31 if ($this->_generate_conf() === false) { 32 $this->success = false; 33 return; 34 } 35 } 36 37 if (!file_exists(DOKU_INC . 'api/uc.php') || !is_dir(DOKU_INC . 'uc_client') || !file_exists(DOKU_INC . 'conf/uc.auth.php') || !file_exists(DOKU_INC . 'uc_client/client.php')) { 38 msg($this->getLang('ucfilecheckfail'), -1); 39 $this->success = false; 40 return; 41 } 42 43 require_once(DOKU_INC.'conf/uc.auth.php'); 44 require_once(DOKU_INC.'uc_client/client.php'); 45 46 // FIXME set capabilities accordingly 47 $this->cando['addUser'] = false; // can Users be created? 48 $this->cando['delUser'] = false; // can Users be deleted? 49 $this->cando['modLogin'] = false; // can login names be changed? 50 $this->cando['modPass'] = false; // can passwords be changed? 51 $this->cando['modName'] = false; // can real names be changed? 52 $this->cando['modMail'] = false; // can emails be changed? 53 $this->cando['modGroups'] = false; // can groups be changed? 54 $this->cando['getUsers'] = false; // can a (filtered) list of users be retrieved? 55 $this->cando['getUserCount']= false; // can the number of users be retrieved? 56 $this->cando['getGroups'] = false; // can a list of available groups be retrieved? 57 $this->cando['external'] = true; // does the module do external auth checking? 58 $this->cando['logout'] = true; // can the user logout again? (eg. not possible with HTTP auth) 59 60 // FIXME intialize your auth system and set success to true, if successful 61 $this->success = true; 62 } 63 64 65 /** 66 * Log off the current user [ OPTIONAL ] 67 */ 68 public function logOff() { 69 $this->_uc_setcookie($this->getConf('cookiename'), '', -1); 70 uc_user_synlogout(); 71 msg($this->getLang('logoutsuccess'), 0); 72 } 73 74 /** 75 * Do all authentication [ OPTIONAL ] 76 * 77 * @param string $user Username 78 * @param string $pass Cleartext Password 79 * @param bool $sticky Cookie should not expire 80 * @return bool true on successful auth 81 */ 82 public function trustExternal($user, $pass, $sticky = false) { 83 global $USERINFO; 84 $sticky ? $sticky = true : $sticky = false; //sanity check 85 86 // do the checking here 87 $uid = ''; 88 $username = ''; 89 $password = ''; 90 $email = ''; 91 $checked = false; 92 $user_info = array(); 93 94 if(!empty($user)){ 95 list($uid, $username, $password, $email) = $this->_uc_user_login($user, $pass); 96 setcookie($this->getConf('cookiename'), '', -86400); 97 if($uid > 0){ 98 $_SERVER['REMOTE_USER'] = $username; 99 $user_info = $this->_uc_get_user_full($uid, 1); 100 $password = $user_info['password']; 101 $this->_uc_setcookie($this->getConf('cookiename'), uc_authcode($uid."\t".$user_info['password']."\t".$this->_convert_charset($username), 'ENCODE')); 102 uc_user_synlogin($uid); 103 $checked = true; 104 }else{ 105 msg($this->getLang('loginfail'), -1); 106 $checked = false; 107 } 108 }else{ 109 $cookie = $_COOKIE[$this->getConf('cookiename')]; 110 if(!empty($cookie)){ 111 // use password check instead of username check. 112 list($uid, $password, $username) = explode("\t", uc_authcode($cookie, 'DECODE')); 113 $username = $this->_convert_charset($username, 0); 114 if($password && $uid && $username){ 115 // get session info 116 $session = $_SESSION[DOKU_COOKIE]['auth']; 117 if(isset($session) && $session['user'] == $username && $session['password'] == $password){ 118 $user_info = $session['info']; 119 $user = $user_info['name']; 120 $email = $user_info['mail']; 121 $group = $user_info['grps']; 122 $checked = true; 123 }else{ 124 $user_info = $this->_uc_get_user_full($uid, 1); 125 if($uid == $user_info['uid'] && $password == $user_info['password']){ 126 // he has logged in from other uc apps 127 $user = $user_info['username']; 128 $email = $user_info['email']; 129 $checked = true; 130 } 131 } 132 133 } 134 } 135 } 136 137 if($checked == true){ 138 $USERINFO['name'] = $user; 139 $USERINFO['mail'] = $email; 140 $USERINFO['grps'] = array('user'); 141 $_SERVER['REMOTE_USER'] = $user; 142 $_SESSION[DOKU_COOKIE]['auth']['user'] = $user; 143 $_SESSION[DOKU_COOKIE]['auth']['pass'] = $pass; 144 $_SESSION[DOKU_COOKIE]['auth']['password'] = $password; 145 $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO; 146 } 147 return $checked; 148 } 149 150 /** 151 * Check user+password 152 * 153 * May be ommited if trustExternal is used. 154 * 155 * @param string $user the user name 156 * @param string $pass the clear text password 157 * @return bool 158 */ 159 public function checkPass($user, $pass) { 160 return $this->_uc_user_login($user, $pass); // return true if okay 161 } 162 163 /** 164 * Return user info 165 * 166 * Returns info about the given user needs to contain 167 * at least these fields: 168 * 169 * name string full name of the user 170 * mail string email addres of the user 171 * grps array list of groups the user is in 172 * 173 * @param string $user the user name 174 * @return array containing user data or false 175 */ 176 public function getUserData($user) { 177 $user_info = false; 178 if($data = $this->_uc_get_user($user)){ 179 list($uid, $username, $email) = $data; 180 $user_info = array( 181 'name' => $username, 182 'mail' => $email, 183 'grps' => $this->_get_user_group($uid, 1), 184 'uid' => $uid 185 ); 186 } 187 return $user_info; 188 } 189 190 /** 191 * Create a new User [implement only where required/possible] 192 * 193 * Returns false if the user already exists, null when an error 194 * occurred and true if everything went well. 195 * 196 * The new user HAS TO be added to the default group by this 197 * function! 198 * 199 * Set addUser capability when implemented 200 * 201 * @param string $user 202 * @param string $pass 203 * @param string $name 204 * @param string $mail 205 * @param null|array $grps 206 * @return bool|null 207 */ 208 public function createUser($user, $pass, $name, $mail, $grps = null) { 209 return $this->_uc_user_register($user, $pass, $mail); 210 } 211 212 /** 213 * Modify user data [implement only where required/possible] 214 * 215 * Set the mod* capabilities according to the implemented features 216 * 217 * @param string $user nick of the user to be changed 218 * @param array $changes array of field/value pairs to be changed (password will be clear text) 219 * @return bool 220 */ 221 public function modifyUser($user, $changes) { 222 if(!is_array($changes) || !count($changes)){ 223 return true; 224 } 225 $ucresult = $this->_uc_user_edit($user, $_POST['oldpass'], $changes['pass'] ? $changes['pass'] : '', $changes['mail'] ? $changes['mail'] : ''); 226 $msg = ''; 227 switch($ucresult){ 228 case 1: 229 case 0: 230 case -7: 231 return true; 232 break; 233 case -1: 234 $msg = 'wrongpassword'; 235 break; 236 case -4: 237 $msg = 'wrongemailformat'; 238 break; 239 case -5: 240 $msg = 'emailforbidden'; 241 break; 242 case -6: 243 $msg = 'emailregistered'; 244 break; 245 case -8: 246 $msg = 'userprotected'; 247 break; 248 } 249 msg($this->getLang($msg), -1); 250 return false; 251 } 252 253 /** 254 * Delete one or more users [implement only where required/possible] 255 * 256 * Set delUser capability when implemented 257 * 258 * @param array $users 259 * @return int number of users deleted 260 */ 261 public function deleteUsers($users) { 262 $count = 0; 263 if(is_array($users) && count($users)){ 264 foreach($users as $user){ 265 $uid = $this->get_uid($user); 266 if($uid && uc_user_delete($uid)){ 267 $count++; 268 } 269 } 270 } 271 return $count; 272 } 273 274 /** 275 * Bulk retrieval of user data [implement only where required/possible] 276 * 277 * Set getUsers capability when implemented 278 * 279 * @param int $start index of first user to be returned 280 * @param int $limit max number of users to be returned 281 * @param array $filter array of field/pattern pairs, null for no filter 282 * @return array list of userinfo (refer getUserData for internal userinfo details) 283 */ 284 //public function retrieveUsers($start = 0, $limit = -1, $filter = null) { 285 // FIXME implement 286 // return array(); 287 //} 288 289 /** 290 * Return a count of the number of user which meet $filter criteria 291 * [should be implemented whenever retrieveUsers is implemented] 292 * 293 * Set getUserCount capability when implemented 294 * 295 * @param array $filter array of field/pattern pairs, empty array for no filter 296 * @return int 297 */ 298 //public function getUserCount($filter = array()) { 299 // FIXME implement 300 // return 0; 301 //} 302 303 /** 304 * Define a group [implement only where required/possible] 305 * 306 * Set addGroup capability when implemented 307 * 308 * @param string $group 309 * @return bool 310 */ 311 //public function addGroup($group) { 312 // FIXME implement 313 // return false; 314 //} 315 316 /** 317 * Retrieve groups [implement only where required/possible] 318 * 319 * Set getGroups capability when implemented 320 * 321 * @param int $start 322 * @param int $limit 323 * @return array 324 */ 325 //public function retrieveGroups($start = 0, $limit = 0) { 326 // FIXME implement 327 // return array(); 328 //} 329 330 /** 331 * Return case sensitivity of the backend 332 * 333 * When your backend is caseinsensitive (eg. you can login with USER and 334 * user) then you need to overwrite this method and return false 335 * 336 * @return bool 337 */ 338 public function isCaseSensitive() { 339 return true; 340 } 341 342 /** 343 * Sanitize a given username 344 * 345 * This function is applied to any user name that is given to 346 * the backend and should also be applied to any user name within 347 * the backend before returning it somewhere. 348 * 349 * This should be used to enforce username restrictions. 350 * 351 * @param string $user username 352 * @return string the cleaned username 353 */ 354 public function cleanUser($user) { 355 return $user; 356 } 357 358 /** 359 * Sanitize a given groupname 360 * 361 * This function is applied to any groupname that is given to 362 * the backend and should also be applied to any groupname within 363 * the backend before returning it somewhere. 364 * 365 * This should be used to enforce groupname restrictions. 366 * 367 * Groupnames are to be passed without a leading '@' here. 368 * 369 * @param string $group groupname 370 * @return string the cleaned groupname 371 */ 372 public function cleanGroup($group) { 373 return $group; 374 } 375 376 /** 377 * Check Session Cache validity [implement only where required/possible] 378 * 379 * DokuWiki caches user info in the user's session for the timespan defined 380 * in $conf['auth_security_timeout']. 381 * 382 * This makes sure slow authentication backends do not slow down DokuWiki. 383 * This also means that changes to the user database will not be reflected 384 * on currently logged in users. 385 * 386 * To accommodate for this, the user manager plugin will touch a reference 387 * file whenever a change is submitted. This function compares the filetime 388 * of this reference file with the time stored in the session. 389 * 390 * This reference file mechanism does not reflect changes done directly in 391 * the backend's database through other means than the user manager plugin. 392 * 393 * Fast backends might want to return always false, to force rechecks on 394 * each page load. Others might want to use their own checking here. If 395 * unsure, do not override. 396 * 397 * @param string $user - The username 398 * @return bool 399 */ 400 //public function useSessionCache($user) { 401 // FIXME implement 402 //} 403 404 /** 405 * get user id frome ucenter 406 * 407 * @param string $username the name of the user 408 * @return int the user id. 0 on error. 409 */ 410 function get_uid($username){ 411 $uid = 0; 412 if($data = $this->_uc_get_user($username)) { 413 $uid = $data[0]; 414 } 415 return $uid; 416 } 417 418 private function _get_user_group($user, $is_uid = 0) { 419 return array('user'); 420 } 421 422 /** 423 * convert charset 424 * @param string $str the string that to be converted. 425 * @param bool $out 1: doku convert to other char, 0: other char convert to doku 426 * @return string converted string. 427 */ 428 private function _convert_charset($str, $out = 1){ 429 if($this->getConf('uccharset') != 'utf-8'){ 430 $str = $out ? iconv('utf-8', $this->getConf('uccharset'), $str) : iconv($this->getConf('uccharset'), 'utf-8', $str); 431 } 432 return $str; 433 } 434 435 private function _convert_charset_all($arr, $out = 1){ 436 if($this->getConf('uccharset') != 'utf-8'){ 437 if(is_array($arr)){ 438 foreach($arr as $k=>$v){ 439 $arr[$k] = $this->_convert_charset_all($v, $out); 440 } 441 }else{ 442 $arr = $this->_convert_charset($arr, $out); 443 } 444 } 445 return $arr; 446 } 447 448 private function _uc_user_login($username, $password){ 449 $return = uc_user_login($this->_convert_charset($username), $password); 450 return array($return[0], $this->_convert_charset($return[1], 0), $return[2], $return[3], $return[4]); 451 } 452 453 private function _uc_get_user($username, $isuid = 0){ 454 $return = uc_get_user($this->_convert_charset($username), $isuid); 455 return array($return[0], $this->_convert_charset($return[1], 0), $return[2]); 456 } 457 458 private function _uc_user_register($username, $password, $email){ 459 return uc_user_register($this->_convert_charset($username), $password, $email); 460 } 461 462 private function _uc_user_edit($username, $oldpw, $newpw, $email){ 463 return uc_user_edit($this->_convert_charset($username), $oldpw, $newpw, $email, 0); 464 } 465 466 private function _uc_setcookie($var, $value = '', $life = 0, $httponly = false) { 467 468 $_COOKIE[$var] = $value; 469 470 $timestamp = time(); 471 472 if($value == '' || $life < 0) { 473 $value = ''; 474 $life = -1; 475 } 476 477 $life = $life > 0 ? $timestamp + $life : ($life < 0 ? $timestamp - 31536000 : 0); 478 $path = $httponly && PHP_VERSION < '5.2.0' ? $this->getConf('cookiepath').'; HttpOnly' : $this->getConf('cookiepath'); 479 480 $secure = $_SERVER['SERVER_PORT'] == 443 ? 1 : 0; 481 if(PHP_VERSION < '5.2.0') { 482 setcookie($var, $value, $life, $path, $this->getConf('cookiedomain'), $secure); 483 } else { 484 setcookie($var, $value, $life, $path, $this->getConf('cookiedomain'), $secure, $httponly); 485 } 486 } 487 488 private function _uc_get_user_full($username, $isuid = 0){ 489 global $uc_controls; 490 if(empty($uc_controls['user'])){ 491 require_once(DOKU_INC.'/uc_client/lib/db.class.php'); 492 require_once(DOKU_INC.'/uc_client/model/base.php'); 493 require_once(DOKU_INC.'/uc_client/control/user.php'); 494 $uc_controls['user'] = new usercontrol(); 495 } 496 $args = uc_addslashes(array('username' => $username, 'isuid' => $isuid), 1, TRUE); 497 $uc_controls['user']->input = $args; 498 $uc_controls['user']->init_input(); 499 $username = $uc_controls['user']->input('username'); 500 if(!$uc_controls['user']->input('isuid')) { 501 $status = $_ENV['user']->get_user_by_username($username); 502 } else { 503 $status = $_ENV['user']->get_user_by_uid($username); 504 } 505 if($status) { 506 // do not return salt. 507 return array( 508 'uid' => $status['uid'], 509 'username' => $status['username'], 510 'grps' => $this->_get_user_group($status['uid'], 1), 511 'password' => $status['password'], 512 'email' => $status['email'], 513 'regip' => $status['regip'], 514 'regdate' => $status['regdate'], 515 'lastloginip' => $status['lastloginip'], 516 'lastlogintime' => $status['lastlogintime'] 517 ); 518 } else { 519 return 0; 520 } 521 } 522 523 private function _recurse_copy($src,$dst) { 524 $dir = opendir($src); 525 @mkdir($dst); 526 while(false !== ( $file = readdir($dir)) ) { 527 if (( $file != '.' ) && ( $file != '..' )) { 528 if ( is_dir($src . '/' . $file) ) { 529 $this->_recurse_copy($src . '/' . $file,$dst . '/' . $file); 530 } 531 else { 532 copy($src . '/' . $file,$dst . '/' . $file); 533 } 534 } 535 } 536 closedir($dir); 537 } 538 539 /** 540 * generate DOKU_INC/conf/uc.auth.php config according to user config in Admin Settings. 541 * @return bool 542 */ 543 private function _generate_conf() { 544 $uc_conf = '<?php' . "\n\n" . $this->getConf('ucappconfig'). "\n\n"; 545 $conf_keys = array('COOKIE_PATH', 'COOKIE_DOMAIN', 'COOKIE_NAME'); 546 foreach($conf_keys as $conf_name){ 547 $conf_value = $this->getConf(str_replace('_', '', strtolower($conf_name))); 548 $uc_conf .= "define('DW_UC_$conf_name', '$conf_value');". "\n\n"; 549 } 550 $uc_conf .= "\n\n" . '?>'; 551 $handle = fopen(DOKU_INC . 'conf/uc.auth.php', 'w'); 552 if ($handle === false) { 553 msg($this->getLang('openucconfigfail'), -1); 554 return false; 555 } 556 if (fwrite($handle, $uc_conf) === false) { 557 msg($this->getLang('writeucconfigfail'), -1); 558 return false; 559 } 560 return fclose($handle); 561 } 562} 563 564// vim:ts=4:sw=4:et: