[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/lib/plugins/authpgsql/ -> auth.php (source)

   1  <?php
   2  // must be run within Dokuwiki
   3  if(!defined('DOKU_INC')) die();
   4  
   5  /**
   6   * PostgreSQL authentication backend
   7   *
   8   * This class inherits much functionality from the MySQL class
   9   * and just reimplements the Postgres specific parts.
  10   *
  11   * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
  12   * @author     Andreas Gohr <andi@splitbrain.org>
  13   * @author     Chris Smith <chris@jalakai.co.uk>
  14   * @author     Matthias Grimm <matthias.grimmm@sourceforge.net>
  15   * @author     Jan Schumann <js@schumann-it.com>
  16   */
  17  class auth_plugin_authpgsql extends auth_plugin_authmysql {
  18  
  19      /**
  20       * Constructor
  21       *
  22       * checks if the pgsql interface is available, otherwise it will
  23       * set the variable $success of the basis class to false
  24       *
  25       * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  26       * @author Andreas Gohr <andi@splitbrain.org>
  27       */
  28      public function __construct() {
  29          // we don't want the stuff the MySQL constructor does, but the grandparent might do something
  30          DokuWiki_Auth_Plugin::__construct();
  31  
  32          if(!function_exists('pg_connect')) {
  33              $this->_debug("PgSQL err: PHP Postgres extension not found.", -1, __LINE__, __FILE__);
  34              $this->success = false;
  35              return;
  36          }
  37  
  38          $this->loadConfig();
  39  
  40          // set capabilities based upon config strings set
  41          if(empty($this->conf['user']) ||
  42              empty($this->conf['password']) || empty($this->conf['database'])
  43          ) {
  44              $this->_debug("PgSQL err: insufficient configuration.", -1, __LINE__, __FILE__);
  45              $this->success = false;
  46              return;
  47          }
  48  
  49          $this->cando['addUser']   = $this->_chkcnf(
  50              array(
  51                   'getUserInfo',
  52                   'getGroups',
  53                   'addUser',
  54                   'getUserID',
  55                   'getGroupID',
  56                   'addGroup',
  57                   'addUserGroup'
  58              )
  59          );
  60          $this->cando['delUser']   = $this->_chkcnf(
  61              array(
  62                   'getUserID',
  63                   'delUser',
  64                   'delUserRefs'
  65              )
  66          );
  67          $this->cando['modLogin']  = $this->_chkcnf(
  68              array(
  69                   'getUserID',
  70                   'updateUser',
  71                   'UpdateTarget'
  72              )
  73          );
  74          $this->cando['modPass']   = $this->cando['modLogin'];
  75          $this->cando['modName']   = $this->cando['modLogin'];
  76          $this->cando['modMail']   = $this->cando['modLogin'];
  77          $this->cando['modGroups'] = $this->_chkcnf(
  78              array(
  79                   'getUserID',
  80                   'getGroups',
  81                   'getGroupID',
  82                   'addGroup',
  83                   'addUserGroup',
  84                   'delGroup',
  85                   'getGroupID',
  86                   'delUserGroup'
  87              )
  88          );
  89          /* getGroups is not yet supported
  90             $this->cando['getGroups']    = $this->_chkcnf(array('getGroups',
  91             'getGroupID')); */
  92          $this->cando['getUsers']     = $this->_chkcnf(
  93              array(
  94                   'getUsers',
  95                   'getUserInfo',
  96                   'getGroups'
  97              )
  98          );
  99          $this->cando['getUserCount'] = $this->_chkcnf(array('getUsers'));
 100      }
 101  
 102      /**
 103       * Check if the given config strings are set
 104       *
 105       * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 106       *
 107       * @param   string[] $keys
 108       * @param   bool  $wop
 109       * @return  bool
 110       */
 111      protected function _chkcnf($keys, $wop = false) {
 112          foreach($keys as $key) {
 113              if(empty($this->conf[$key])) return false;
 114          }
 115          return true;
 116      }
 117  
 118      /**
 119       * Counts users which meet certain $filter criteria.
 120       *
 121       * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 122       *
 123       * @param  array  $filter  filter criteria in item/pattern pairs
 124       * @return int count of found users.
 125       */
 126      public function getUserCount($filter = array()) {
 127          $rc = 0;
 128  
 129          if($this->_openDB()) {
 130              $sql = $this->_createSQLFilter($this->conf['getUsers'], $filter);
 131  
 132              // no equivalent of SQL_CALC_FOUND_ROWS in pgsql?
 133              if(($result = $this->_queryDB($sql))) {
 134                  $rc = count($result);
 135              }
 136              $this->_closeDB();
 137          }
 138          return $rc;
 139      }
 140  
 141      /**
 142       * Bulk retrieval of user data
 143       *
 144       * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 145       *
 146       * @param   int   $first     index of first user to be returned
 147       * @param   int   $limit     max number of users to be returned
 148       * @param   array $filter    array of field/pattern pairs
 149       * @return  array userinfo (refer getUserData for internal userinfo details)
 150       */
 151      public function retrieveUsers($first = 0, $limit = 0, $filter = array()) {
 152          $out = array();
 153  
 154          if($this->_openDB()) {
 155              $this->_lockTables("READ");
 156              $sql = $this->_createSQLFilter($this->conf['getUsers'], $filter);
 157              $sql .= " ".$this->conf['SortOrder'];
 158              if($limit) $sql .= " LIMIT $limit";
 159              if($first) $sql .= " OFFSET $first";
 160              $result = $this->_queryDB($sql);
 161  
 162              foreach($result as $user) {
 163                  if(($info = $this->_getUserInfo($user['user']))) {
 164                      $out[$user['user']] = $info;
 165                  }
 166              }
 167  
 168              $this->_unlockTables();
 169              $this->_closeDB();
 170          }
 171          return $out;
 172      }
 173  
 174      // @inherit function joinGroup($user, $group)
 175      // @inherit function leaveGroup($user, $group) {
 176  
 177      /**
 178       * Adds a user to a group.
 179       *
 180       * If $force is set to true non existing groups would be created.
 181       *
 182       * The database connection must already be established. Otherwise
 183       * this function does nothing and returns 'false'.
 184       *
 185       * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 186       * @author Andreas Gohr   <andi@splitbrain.org>
 187       *
 188       * @param   string $user    user to add to a group
 189       * @param   string $group   name of the group
 190       * @param   bool   $force   create missing groups
 191       * @return  bool   true on success, false on error
 192       */
 193      protected function _addUserToGroup($user, $group, $force = false) {
 194          $newgroup = 0;
 195  
 196          if(($this->dbcon) && ($user)) {
 197              $gid = $this->_getGroupID($group);
 198              if(!$gid) {
 199                  if($force) { // create missing groups
 200                      $sql = str_replace('%{group}', addslashes($group), $this->conf['addGroup']);
 201                      $this->_modifyDB($sql);
 202                      //group should now exists try again to fetch it
 203                      $gid      = $this->_getGroupID($group);
 204                      $newgroup = 1; // group newly created
 205                  }
 206              }
 207              if(!$gid) return false; // group didn't exist and can't be created
 208  
 209              $sql = $this->conf['addUserGroup'];
 210              if(strpos($sql, '%{uid}') !== false) {
 211                  $uid = $this->_getUserID($user);
 212                  $sql = str_replace('%{uid}', addslashes($uid), $sql);
 213              }
 214              $sql = str_replace('%{user}', addslashes($user), $sql);
 215              $sql = str_replace('%{gid}', addslashes($gid), $sql);
 216              $sql = str_replace('%{group}', addslashes($group), $sql);
 217              if($this->_modifyDB($sql) !== false) {
 218                  $this->_flushUserInfoCache($user);
 219                  return true;
 220              }
 221  
 222              if($newgroup) { // remove previously created group on error
 223                  $sql = str_replace('%{gid}', addslashes($gid), $this->conf['delGroup']);
 224                  $sql = str_replace('%{group}', addslashes($group), $sql);
 225                  $this->_modifyDB($sql);
 226              }
 227          }
 228          return false;
 229      }
 230  
 231      // @inherit function _delUserFromGroup($user $group)
 232      // @inherit function _getGroups($user)
 233      // @inherit function _getUserID($user)
 234  
 235      /**
 236       * Adds a new User to the database.
 237       *
 238       * The database connection must already be established
 239       * for this function to work. Otherwise it will return
 240       * 'false'.
 241       *
 242       * @param  string $user  login of the user
 243       * @param  string $pwd   encrypted password
 244       * @param  string $name  full name of the user
 245       * @param  string $mail  email address
 246       * @param  array  $grps  array of groups the user should become member of
 247       * @return bool
 248       *
 249       * @author  Andreas Gohr <andi@splitbrain.org>
 250       * @author  Chris Smith <chris@jalakai.co.uk>
 251       * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 252       */
 253      protected function _addUser($user, $pwd, $name, $mail, $grps) {
 254          if($this->dbcon && is_array($grps)) {
 255              $sql = str_replace('%{user}', addslashes($user), $this->conf['addUser']);
 256              $sql = str_replace('%{pass}', addslashes($pwd), $sql);
 257              $sql = str_replace('%{name}', addslashes($name), $sql);
 258              $sql = str_replace('%{email}', addslashes($mail), $sql);
 259              if($this->_modifyDB($sql)) {
 260                  $uid = $this->_getUserID($user);
 261              } else {
 262                  return false;
 263              }
 264  
 265              $group = '';
 266              $gid = false;
 267  
 268              if($uid) {
 269                  foreach($grps as $group) {
 270                      $gid = $this->_addUserToGroup($user, $group, true);
 271                      if($gid === false) break;
 272                  }
 273  
 274                  if($gid !== false){
 275                      $this->_flushUserInfoCache($user);
 276                      return true;
 277                  } else {
 278                      /* remove the new user and all group relations if a group can't
 279                       * be assigned. Newly created groups will remain in the database
 280                       * and won't be removed. This might create orphaned groups but
 281                       * is not a big issue so we ignore this problem here.
 282                       */
 283                      $this->_delUser($user);
 284                      $this->_debug("PgSQL err: Adding user '$user' to group '$group' failed.", -1, __LINE__, __FILE__);
 285                  }
 286              }
 287          }
 288          return false;
 289      }
 290  
 291      /**
 292       * Opens a connection to a database and saves the handle for further
 293       * usage in the object. The successful call to this functions is
 294       * essential for most functions in this object.
 295       *
 296       * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 297       *
 298       * @return bool
 299       */
 300      protected function _openDB() {
 301          if(!$this->dbcon) {
 302              $dsn = $this->conf['server'] ? 'host='.$this->conf['server'] : '';
 303              $dsn .= ' port='.$this->conf['port'];
 304              $dsn .= ' dbname='.$this->conf['database'];
 305              $dsn .= ' user='.$this->conf['user'];
 306              $dsn .= ' password='.conf_decodeString($this->conf['password']);
 307  
 308              $con = @pg_connect($dsn);
 309              if($con) {
 310                  $this->dbcon = $con;
 311                  return true; // connection and database successfully opened
 312              } else {
 313                  $this->_debug(
 314                          "PgSQL err: Connection to {$this->conf['user']}@{$this->conf['server']} not possible.",
 315                          -1, __LINE__, __FILE__
 316                      );
 317              }
 318              return false; // connection failed
 319          }
 320          return true; // connection already open
 321      }
 322  
 323      /**
 324       * Closes a database connection.
 325       *
 326       * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 327       */
 328      protected function _closeDB() {
 329          if($this->dbcon) {
 330              pg_close($this->dbcon);
 331              $this->dbcon = 0;
 332          }
 333      }
 334  
 335      /**
 336       * Sends a SQL query to the database and transforms the result into
 337       * an associative array.
 338       *
 339       * This function is only able to handle queries that returns a
 340       * table such as SELECT.
 341       *
 342       * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 343       *
 344       * @param  string $query  SQL string that contains the query
 345       * @return array|false the result table
 346       */
 347      protected function _queryDB($query) {
 348          $resultarray = array();
 349          if($this->dbcon) {
 350              $result = @pg_query($this->dbcon, $query);
 351              if($result) {
 352                  while(($t = pg_fetch_assoc($result)) !== false)
 353                      $resultarray[] = $t;
 354                  pg_free_result($result);
 355                  return $resultarray;
 356              } else{
 357                  $this->_debug('PgSQL err: '.pg_last_error($this->dbcon), -1, __LINE__, __FILE__);
 358              }
 359          }
 360          return false;
 361      }
 362  
 363      /**
 364       * Executes an update or insert query. This differs from the
 365       * MySQL one because it does NOT return the last insertID
 366       *
 367       * @author Andreas Gohr <andi@splitbrain.org>
 368       *
 369       * @param string $query
 370       * @return bool
 371       */
 372      protected function _modifyDB($query) {
 373          if($this->dbcon) {
 374              $result = @pg_query($this->dbcon, $query);
 375              if($result) {
 376                  pg_free_result($result);
 377                  return true;
 378              }
 379              $this->_debug('PgSQL err: '.pg_last_error($this->dbcon), -1, __LINE__, __FILE__);
 380          }
 381          return false;
 382      }
 383  
 384      /**
 385       * Start a transaction
 386       *
 387       * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 388       *
 389       * @param string $mode  could be 'READ' or 'WRITE'
 390       * @return bool
 391       */
 392      protected function _lockTables($mode) {
 393          if($this->dbcon) {
 394              $this->_modifyDB('BEGIN');
 395              return true;
 396          }
 397          return false;
 398      }
 399  
 400      /**
 401       * Commit a transaction
 402       *
 403       * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 404       *
 405       * @return bool
 406       */
 407      protected function _unlockTables() {
 408          if($this->dbcon) {
 409              $this->_modifyDB('COMMIT');
 410              return true;
 411          }
 412          return false;
 413      }
 414  
 415      /**
 416       * Escape a string for insertion into the database
 417       *
 418       * @author Andreas Gohr <andi@splitbrain.org>
 419       *
 420       * @param  string  $string The string to escape
 421       * @param  bool    $like   Escape wildcard chars as well?
 422       * @return string
 423       */
 424      protected function _escape($string, $like = false) {
 425          $string = pg_escape_string($string);
 426          if($like) {
 427              $string = addcslashes($string, '%_');
 428          }
 429          return $string;
 430      }
 431  }