[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/auth/ -> mysql.class.php (source)

   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        }