3 * PgSQL authentication backend
5 * This class inherits much functionality from the MySQL class
6 * and just reimplements the Postgres specific parts.
8 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
9 * @author Andreas Gohr <andi@splitbrain.org>
10 * @author Chris Smith <chris@jalakai.co.uk>
11 * @author Matthias Grimm <matthias.grimmm@sourceforge.net>
14 require_once(DOKU_INC.'inc/auth/mysql.class.php');
16 class auth_pgsql extends auth_mysql {
21 * checks if the pgsql interface is available, otherwise it will
22 * set the variable $success of the basis class to false
24 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
25 * @author Andreas Gohr <andi@splitbrain.org>
27 function __construct() {
29 $this->cnf = $conf['auth']['pgsql'];
30 if(!$this->cnf['port']){
31 $this->cnf['port'] = 5432;
34 if (method_exists($this, 'auth_basic')){
38 if(!function_exists('pg_connect')) {
39 if ($this->cnf['debug'])
40 msg("PgSQL err: PHP Postgres extension not found.",-1);
41 $this->success = false;
45 $this->defaultgroup = $conf['defaultgroup'];
47 // set capabilities based upon config strings set
48 if (empty($this->cnf['user']) ||
49 empty($this->cnf['password']) || empty($this->cnf['database'])){
50 if ($this->cnf['debug']){
51 msg("PgSQL err: insufficient configuration.",-1,__LINE__,__FILE__);
53 $this->success = false;
57 $this->cando['addUser'] = $this->_chkcnf(array(
65 $this->cando['delUser'] = $this->_chkcnf(array(
69 $this->cando['modLogin'] = $this->_chkcnf(array(
73 $this->cando['modPass'] = $this->cando['modLogin'];
74 $this->cando['modName'] = $this->cando['modLogin'];
75 $this->cando['modMail'] = $this->cando['modLogin'];
76 $this->cando['modGroups'] = $this->_chkcnf(array(
85 /* getGroups is not yet supported
86 $this->cando['getGroups'] = $this->_chkcnf(array('getGroups',
88 $this->cando['getUsers'] = $this->_chkcnf(array(
92 $this->cando['getUserCount'] = $this->_chkcnf(array('getUsers'));
96 * Check if the given config strings are set
98 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
101 function _chkcnf($keys, $wop=false){
102 foreach ($keys as $key){
103 if (empty($this->cnf[$key])) return false;
108 // @inherit function checkPass($user,$pass)
109 // @inherit function getUserData($user)
110 // @inherit function createUser($user,$pwd,$name,$mail,$grps=null)
111 // @inherit function modifyUser($user, $changes)
112 // @inherit function deleteUsers($users)
118 * Counts users which meet certain $filter criteria.
120 * @param array $filter filter criteria in item/pattern pairs
121 * @return count of found users.
123 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
125 function getUserCount($filter=array()) {
128 if($this->_openDB()) {
129 $sql = $this->_createSQLFilter($this->cnf['getUsers'], $filter);
131 // no equivalent of SQL_CALC_FOUND_ROWS in pgsql?
132 if (($result = $this->_queryDB($sql))){
133 $rc = count($result);
141 * Bulk retrieval of user data. [public function]
143 * @param first index of first user to be returned
144 * @param limit max number of users to be returned
145 * @param filter array of field/pattern pairs
146 * @return array of userinfo (refer getUserData for internal userinfo details)
148 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
150 function retrieveUsers($first=0,$limit=10,$filter=array()) {
153 if($this->_openDB()) {
154 $this->_lockTables("READ");
155 $sql = $this->_createSQLFilter($this->cnf['getUsers'], $filter);
156 $sql .= " ".$this->cnf['SortOrder']." LIMIT $limit OFFSET $first";
157 $result = $this->_queryDB($sql);
159 foreach ($result as $user)
160 if (($info = $this->_getUserInfo($user['user'])))
161 $out[$user['user']] = $info;
163 $this->_unlockTables();
169 // @inherit function joinGroup($user, $group)
170 // @inherit function leaveGroup($user, $group) {
173 * Adds a user to a group.
175 * If $force is set to '1' non existing groups would be created.
177 * The database connection must already be established. Otherwise
178 * this function does nothing and returns 'false'.
180 * @param $user user to add to a group
181 * @param $group name of the group
182 * @param $force '1' create missing groups
183 * @return bool 'true' on success, 'false' on error
185 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
186 * @author Andreas Gohr <andi@splitbrain.org>
188 function _addUserToGroup($user, $group, $force=0) {
191 if (($this->dbcon) && ($user)) {
192 $gid = $this->_getGroupID($group);
194 if ($force) { // create missing groups
195 $sql = str_replace('%{group}',addslashes($group),$this->cnf['addGroup']);
196 $this->_modifyDB($sql);
197 //group should now exists try again to fetch it
198 $gid = $this->_getGroupID($group);
199 $newgroup = 1; // group newly created
202 if (!$gid) return false; // group didn't exist and can't be created
204 $sql = $this->cnf['addUserGroup'];
205 if(strpos($sql,'%{uid}') !== false){
206 $uid = $this->_getUserID($user);
207 $sql = str_replace('%{uid}', addslashes($uid), $sql);
209 $sql = str_replace('%{user}', addslashes($user),$sql);
210 $sql = str_replace('%{gid}', addslashes($gid),$sql);
211 $sql = str_replace('%{group}',addslashes($group),$sql);
212 if ($this->_modifyDB($sql) !== false) return true;
214 if ($newgroup) { // remove previously created group on error
215 $sql = str_replace('%{gid}', addslashes($gid),$this->cnf['delGroup']);
216 $sql = str_replace('%{group}',addslashes($group),$sql);
217 $this->_modifyDB($sql);
223 // @inherit function _delUserFromGroup($user $group)
224 // @inherit function _getGroups($user)
225 // @inherit function _getUserID($user)
228 * Adds a new User to the database.
230 * The database connection must already be established
231 * for this function to work. Otherwise it will return
234 * @param $user login of the user
235 * @param $pwd encrypted password
236 * @param $name full name of the user
237 * @param $mail email address
238 * @param $grps array of groups the user should become member of
241 * @author Andreas Gohr <andi@splitbrain.org>
242 * @author Chris Smith <chris@jalakai.co.uk>
243 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
245 function _addUser($user,$pwd,$name,$mail,$grps){
246 if($this->dbcon && is_array($grps)) {
247 $sql = str_replace('%{user}', addslashes($user),$this->cnf['addUser']);
248 $sql = str_replace('%{pass}', addslashes($pwd),$sql);
249 $sql = str_replace('%{name}', addslashes($name),$sql);
250 $sql = str_replace('%{email}',addslashes($mail),$sql);
251 if($this->_modifyDB($sql)){
252 $uid = $this->_getUserID($user);
258 foreach($grps as $group) {
259 $gid = $this->_addUserToGroup($user, $group, 1);
260 if ($gid === false) break;
263 if ($gid) return true;
265 /* remove the new user and all group relations if a group can't
266 * be assigned. Newly created groups will remain in the database
267 * and won't be removed. This might create orphaned groups but
268 * is not a big issue so we ignore this problem here.
270 $this->_delUser($user);
271 if ($this->cnf['debug'])
272 msg("PgSQL err: Adding user '$user' to group '$group' failed.",-1,__LINE__,__FILE__);
279 // @inherit function _delUser($user)
280 // @inherit function _getUserInfo($user)
281 // @inherit function _updateUserInfo($changes, $uid)
282 // @inherit function _getGroupID($group)
285 * Opens a connection to a database and saves the handle for further
286 * usage in the object. The successful call to this functions is
287 * essential for most functions in this object.
291 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
295 $dsn = $this->cnf['server'] ? 'host='.$this->cnf['server'] : '';
296 $dsn .= ' port='.$this->cnf['port'];
297 $dsn .= ' dbname='.$this->cnf['database'];
298 $dsn .= ' user='.$this->cnf['user'];
299 $dsn .= ' password='.$this->cnf['password'];
301 $con = @pg_connect($dsn);
304 return true; // connection and database successfully opened
305 } else if ($this->cnf['debug']){
306 msg ("PgSQL err: Connection to {$this->cnf['user']}@{$this->cnf['server']} not possible.",
307 -1,__LINE__,__FILE__);
309 return false; // connection failed
311 return true; // connection already open
315 * Closes a database connection.
317 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
319 function _closeDB() {
321 pg_close ($this->dbcon);
327 * Sends a SQL query to the database and transforms the result into
328 * an associative array.
330 * This function is only able to handle queries that returns a
331 * table such as SELECT.
333 * @param $query SQL string that contains the query
334 * @return array with the result table
336 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
338 function _queryDB($query) {
340 $result = @pg_query($this->dbcon,$query);
342 while (($t = pg_fetch_assoc($result)) !== false)
344 pg_free_result ($result);
346 }elseif ($this->cnf['debug'])
347 msg('PgSQL err: '.pg_last_error($this->dbcon),-1,__LINE__,__FILE__);
353 * Executes an update or insert query. This differs from the
354 * MySQL one because it does NOT return the last insertID
356 * @author Andreas Gohr
358 function _modifyDB($query) {
360 $result = @pg_query($this->dbcon,$query);
362 pg_free_result ($result);
365 if ($this->cnf['debug']){
366 msg('PgSQL err: '.pg_last_error($this->dbcon),-1,__LINE__,__FILE__);
373 * Start a transaction
375 * @param $mode could be 'READ' or 'WRITE'
376 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
378 function _lockTables($mode) {
380 $this->_modifyDB('BEGIN');
387 * Commit a transaction
389 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
391 function _unlockTables() {
393 $this->_modifyDB('COMMIT');
399 // @inherit function _createSQLFilter($sql, $filter)
403 * Escape a string for insertion into the database
405 * @author Andreas Gohr <andi@splitbrain.org>
406 * @param string $string The string to escape
407 * @param boolean $like Escape wildcard chars as well?
409 function _escape($string,$like=false){
410 $string = pg_escape_string($string);
412 $string = addcslashes($string,'%_');
419 //Setup VIM: ex: et ts=2 :