| [ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * MySQLP authentication backend 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <andi@splitbrain.org> 7 * @author Chris Smith <chris@jalakai.co.uk> 8 * @author Matthias Grimm <matthias.grimmm@sourceforge.net> 9 */ 10 11 define('DOKU_AUTH', dirname(__FILE__)); 12 require_once (DOKU_AUTH.'/basic.class.php'); 13 14 class auth_mysql extends auth_basic { 15 16 var $dbcon = 0; 17 var $dbver = 0; // database version 18 var $dbrev = 0; // database revision 19 var $dbsub = 0; // database subrevision 20 var $cnf = null; 21 var $defaultgroup = ""; 22 23 /** 24 * Constructor 25 * 26 * checks if the mysql interface is available, otherwise it will 27 * set the variable $success of the basis class to false 28 * 29 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 30 */ 31 function auth_mysql() { 32 global $conf; 33 $this->cnf = $conf['auth']['mysql']; 34 35 if (method_exists($this, 'auth_basic')) 36 parent::auth_basic(); 37 38 if(!function_exists('mysql_connect')) { 39 if ($this->cnf['debug']) 40 msg("MySQL err: PHP MySQL extension not found.",-1,__LINE__,__FILE__); 41 $this->success = false; 42 return; 43 } 44 45 // default to UTF-8, you rarely want something else 46 if(!isset($this->cnf['charset'])) $this->cnf['charset'] = 'utf8'; 47 48 $this->defaultgroup = $conf['defaultgroup']; 49 50 // set capabilities based upon config strings set 51 if (empty($this->cnf['server']) || empty($this->cnf['user']) || 52 empty($this->cnf['password']) || empty($this->cnf['database'])){ 53 if ($this->cnf['debug']) 54 msg("MySQL err: insufficient configuration.",-1,__LINE__,__FILE__); 55 $this->success = false; 56 return; 57 } 58 59 $this->cando['addUser'] = $this->_chkcnf(array('getUserInfo', 60 'getGroups', 61 'addUser', 62 'getUserID', 63 'getGroupID', 64 'addGroup', 65 'addUserGroup'),true); 66 $this->cando['delUser'] = $this->_chkcnf(array('getUserID', 67 'delUser', 68 'delUserRefs'),true); 69 $this->cando['modLogin'] = $this->_chkcnf(array('getUserID', 70 'updateUser', 71 'UpdateTarget'),true); 72 $this->cando['modPass'] = $this->cando['modLogin']; 73 $this->cando['modName'] = $this->cando['modLogin']; 74 $this->cando['modMail'] = $this->cando['modLogin']; 75 $this->cando['modGroups'] = $this->_chkcnf(array('getUserID', 76 'getGroups', 77 'getGroupID', 78 'addGroup', 79 'addUserGroup', 80 'delGroup', 81 'getGroupID', 82 'delUserGroup'),true); 83 /* getGroups is not yet supported 84 $this->cando['getGroups'] = $this->_chkcnf(array('getGroups', 85 'getGroupID'),false); */ 86 $this->cando['getUsers'] = $this->_chkcnf(array('getUsers', 87 'getUserInfo', 88 'getGroups'),false); 89 $this->cando['getUserCount'] = $this->_chkcnf(array('getUsers'),false); 90 } 91 92 /** 93 * Check if the given config strings are set 94 * 95 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 96 * @return bool 97 */ 98 function _chkcnf($keys, $wop=false){ 99 foreach ($keys as $key){ 100 if (empty($this->cnf[$key])) return false; 101 } 102 103 /* write operation and lock array filled with tables names? */ 104 if ($wop && (!is_array($this->cnf['TablesToLock']) || 105 !count($this->cnf['TablesToLock']))){ 106 return false; 107 } 108 109 return true; 110 } 111 112 /** 113 * Checks if the given user exists and the given plaintext password 114 * is correct. Furtheron it might be checked wether the user is 115 * member of the right group 116 * 117 * Depending on which SQL string is defined in the config, password 118 * checking is done here (getpass) or by the database (passcheck) 119 * 120 * @param $user user who would like access 121 * @param $pass user's clear text password to check 122 * @return bool 123 * 124 * @author Andreas Gohr <andi@splitbrain.org> 125 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 126 */ 127 function checkPass($user,$pass){ 128 $rc = false; 129 130 if($this->_openDB()) { 131 $sql = str_replace('%{user}',$this->_escape($user),$this->cnf['checkPass']); 132 $sql = str_replace('%{pass}',$this->_escape($pass),$sql); 133 $sql = str_replace('%{dgroup}',$this->_escape($this->defaultgroup),$sql); 134 $result = $this->_queryDB($sql); 135 136 if($result !== false && count($result) == 1) { 137 if($this->cnf['forwardClearPass'] == 1) 138 $rc = true; 139 else 140 $rc = auth_verifyPassword($pass,$result[0]['pass']); 141 } 142 $this->_closeDB(); 143 } 144 return $rc; 145 } 146 147 /** 148 * [public function] 149 * 150 * Returns info about the given user needs to contain 151 * at least these fields: 152 * name string full name of the user 153 * mail string email addres of the user 154 * grps array list of groups the user is in 155 * 156 * @param $user user's nick to get data for 157 * 158 * @author Andreas Gohr <andi@splitbrain.org> 159 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 160 */ 161 function getUserData($user){ 162 if($this->_openDB()) { 163 $this->_lockTables("READ"); 164 $info = $this->_getUserInfo($user); 165 $this->_unlockTables(); 166 $this->_closeDB(); 167 } else 168 $info = false; 169 return $info; 170 } 171 172 /** 173 * [public function] 174 * 175 * Create a new User. Returns false if the user already exists, 176 * null when an error occurred and true if everything went well. 177 * 178 * The new user will be added to the default group by this 179 * function if grps are not specified (default behaviour). 180 * 181 * @param $user nick of the user 182 * @param $pwd clear text password 183 * @param $name full name of the user 184 * @param $mail email address 185 * @param $grps array of groups the user should become member of 186 * 187 * @author Andreas Gohr <andi@splitbrain.org> 188 * @author Chris Smith <chris@jalakai.co.uk> 189 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 190 */ 191 function createUser($user,$pwd,$name,$mail,$grps=null){ 192 if($this->_openDB()) { 193 if (($info = $this->_getUserInfo($user)) !== false) 194 return false; // user already exists 195 196 // set defaultgroup if no groups were given 197 if ($grps == null) 198 $grps = array($this->defaultgroup); 199 200 $this->_lockTables("WRITE"); 201 $pwd = $this->cnf['forwardClearPass'] ? $pwd : auth_cryptPassword($pwd); 202 $rc = $this->_addUser($user,$pwd,$name,$mail,$grps); 203 $this->_unlockTables(); 204 $this->_closeDB(); 205 if ($rc) return true; 206 } 207 return null; // return error 208 } 209 210 /** 211 * Modify user data [public function] 212 * 213 * An existing user dataset will be modified. Changes are given in an array. 214 * 215 * The dataset update will be rejected if the user name should be changed 216 * to an already existing one. 217 * 218 * The password must be provides unencrypted. Pasword cryption is done 219 * automatically if configured. 220 * 221 * If one or more groups could't be updated, an error would be set. In 222 * this case the dataset might already be changed and we can't rollback 223 * the changes. Transactions would be really usefull here. 224 * 225 * modifyUser() may be called without SQL statements defined that are 226 * needed to change group membership (for example if only the user profile 227 * should be modified). In this case we asure that we don't touch groups 228 * even $changes['grps'] is set by mistake. 229 * 230 * @param $user nick of the user to be changed 231 * @param $changes array of field/value pairs to be changed (password 232 * will be clear text) 233 * @return bool true on success, false on error 234 * 235 * @author Chris Smith <chris@jalakai.co.uk> 236 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 237 */ 238 function modifyUser($user, $changes) { 239 $rc = false; 240 241 if (!is_array($changes) || !count($changes)) 242 return true; // nothing to change 243 244 if($this->_openDB()) { 245 $this->_lockTables("WRITE"); 246 247 if (($uid = $this->_getUserID($user))) { 248 $rc = $this->_updateUserInfo($changes, $uid); 249 250 if ($rc && isset($changes['grps']) && $this->cando['modGroups']) { 251 $groups = $this->_getGroups($user); 252 $grpadd = array_diff($changes['grps'], $groups); 253 $grpdel = array_diff($groups, $changes['grps']); 254 255 foreach($grpadd as $group) 256 if (($this->_addUserToGroup($user, $group, 1)) == false) 257 $rc = false; 258 259 foreach($grpdel as $group) 260 if (($this->_delUserFromGroup($user, $group)) == false) 261 $rc = false; 262 } 263 } 264 265 $this->_unlockTables(); 266 $this->_closeDB(); 267 } 268 return $rc; 269 } 270 271 /** 272 * [public function] 273 * 274 * Remove one or more users from the list of registered users 275 * 276 * @param array $users array of users to be deleted 277 * @return int the number of users deleted 278 * 279 * @author Christopher Smith <chris@jalakai.co.uk> 280 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 281 */ 282 function deleteUsers($users) { 283 $count = 0; 284 285 if($this->_openDB()) { 286 if (is_array($users) && count($users)) { 287 $this->_lockTables("WRITE"); 288 foreach ($users as $user) { 289 if ($this->_delUser($user)) 290 $count++; 291 } 292 $this->_unlockTables(); 293 } 294 $this->_closeDB(); 295 } 296 return $count; 297 } 298 299 /** 300 * [public function] 301 * 302 * Counts users which meet certain $filter criteria. 303 * 304 * @param array $filter filter criteria in item/pattern pairs 305 * @return count of found users. 306 * 307 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 308 */ 309 function getUserCount($filter=array()) { 310 $rc = 0; 311 312 if($this->_openDB()) { 313 $sql = $this->_createSQLFilter($this->cnf['getUsers'], $filter); 314 315 if ($this->dbver >= 4) { 316 $sql = substr($sql, 6); /* remove 'SELECT' or 'select' */ 317 $sql = "SELECT SQL_CALC_FOUND_ROWS".$sql." LIMIT 1"; 318 $this->_queryDB($sql); 319 $result = $this->_queryDB("SELECT FOUND_ROWS()"); 320 $rc = $result[0]['FOUND_ROWS()']; 321 } else if (($result = $this->_queryDB($sql))) 322 $rc = count($result); 323 324 $this->_closeDB(); 325 } 326 return $rc; 327 } 328 329 /** 330 * Bulk retrieval of user data. [public function] 331 * 332 * @param first index of first user to be returned 333 * @param limit max number of users to be returned 334 * @param filter array of field/pattern pairs 335 * @return array of userinfo (refer getUserData for internal userinfo details) 336 * 337 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 338 */ 339 function retrieveUsers($first=0,$limit=10,$filter=array()) { 340 $out = array(); 341 342 if($this->_openDB()) { 343 $this->_lockTables("READ"); 344 $sql = $this->_createSQLFilter($this->cnf['getUsers'], $filter); 345 $sql .= " ".$this->cnf['SortOrder']." LIMIT $first, $limit"; 346 $result = $this->_queryDB($sql); 347 348 if (!empty($result)) { 349 foreach ($result as $user) 350 if (($info = $this->_getUserInfo($user['user']))) 351 $out[$user['user']] = $info; 352 } 353 354 $this->_unlockTables(); 355 $this->_closeDB(); 356 } 357 return $out; 358 } 359 360 /** 361 * Give user membership of a group [public function] 362 * 363 * @param $user 364 * @param $group 365 * @return bool true on success, false on error 366 * 367 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 368 */ 369 function joinGroup($user, $group) { 370 $rc = false; 371 372 if ($this->_openDB()) { 373 $this->_lockTables("WRITE"); 374 $rc = $this->_addUserToGroup($user, $group); 375 $this->_unlockTables(); 376 $this->_closeDB(); 377 } 378 return $rc; 379 } 380 381 /** 382 * Remove user from a group [public function] 383 * 384 * @param $user user that leaves a group 385 * @param $group group to leave 386 * @return bool 387 * 388 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 389 */ 390 function leaveGroup($user, $group) { 391 $rc = false; 392 393 if ($this->_openDB()) { 394 $this->_lockTables("WRITE"); 395 $uid = $this->_getUserID($user); 396 $rc = $this->_delUserFromGroup($user, $group); 397 $this->_unlockTables(); 398 $this->_closeDB(); 399 } 400 return $rc; 401 } 402 403 /** 404 * Adds a user to a group. 405 * 406 * If $force is set to '1' non existing groups would be created. 407 * 408 * The database connection must already be established. Otherwise 409 * this function does nothing and returns 'false'. It is strongly 410 * recommended to call this function only after all participating 411 * tables (group and usergroup) have been locked. 412 * 413 * @param $user user to add to a group 414 * @param $group name of the group 415 * @param $force '1' create missing groups 416 * @return bool 'true' on success, 'false' on error 417 * 418 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 419 */ 420 function _addUserToGroup($user, $group, $force=0) { 421 $newgroup = 0; 422 423 if (($this->dbcon) && ($user)) { 424 $gid = $this->_getGroupID($group); 425 if (!$gid) { 426 if ($force) { // create missing groups 427 $sql = str_replace('%{group}',$this->_escape($group),$this->cnf['addGroup']); 428 $gid = $this->_modifyDB($sql); 429 $newgroup = 1; // group newly created 430 } 431 if (!$gid) return false; // group didn't exist and can't be created 432 } 433 434 $sql = $this->cnf['addUserGroup']; 435 if(strpos($sql,'%{uid}') !== false){ 436 $uid = $this->_getUserID($user); 437 $sql = str_replace('%{uid}', $this->_escape($uid),$sql); 438 } 439 $sql = str_replace('%{user}', $this->_escape($user),$sql); 440 $sql = str_replace('%{gid}', $this->_escape($gid),$sql); 441 $sql = str_replace('%{group}',$this->_escape($group),$sql); 442 if ($this->_modifyDB($sql) !== false) return true; 443 444 if ($newgroup) { // remove previously created group on error 445 $sql = str_replace('%{gid}', $this->_escape($gid),$this->cnf['delGroup']); 446 $sql = str_replace('%{group}',$this->_escape($group),$sql); 447 $this->_modifyDB($sql); 448 } 449 } 450 return false; 451 } 452 453 /** 454 * Remove user from a group 455 * 456 * @param $user user that leaves a group 457 * @param $group group to leave 458 * @return bool true on success, false on error 459 * 460 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 461 */ 462 function _delUserFromGroup($user, $group) { 463 $rc = false; 464 465 466 if (($this->dbcon) && ($user)) { 467 $sql = $this->cnf['delUserGroup']; 468 if(strpos($sql,'%{uid}') !== false){ 469 $uid = $this->_getUserID($user); 470 $sql = str_replace('%{uid}', $this->_escape($uid),$sql); 471 } 472 $gid = $this->_getGroupID($group); 473 if ($gid) { 474 $sql = str_replace('%{user}', $this->_escape($user),$sql); 475 $sql = str_replace('%{gid}', $this->_escape($gid),$sql); 476 $sql = str_replace('%{group}',$this->_escape($group),$sql); 477 $rc = $this->_modifyDB($sql) == 0 ? true : false; 478 } 479 } 480 return $rc; 481 } 482 483 /** 484 * Retrieves a list of groups the user is a member off. 485 * 486 * The database connection must already be established 487 * for this function to work. Otherwise it will return 488 * 'false'. 489 * 490 * @param $user user whose groups should be listed 491 * @return bool false on error 492 * @return array array containing all groups on success 493 * 494 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 495 */ 496 function _getGroups($user) { 497 $groups = array(); 498 499 if($this->dbcon) { 500 $sql = str_replace('%{user}',$this->_escape($user),$this->cnf['getGroups']); 501 $result = $this->_queryDB($sql); 502 503 if($result !== false && count($result)) { 504 foreach($result as $row) 505 $groups[] = $row['group']; 506 } 507 return $groups; 508 }