Completed
Push — sidebaracl ( 7a112d...7c3e4a )
by Andreas
04:38
created

lib/plugins/authpgsql/auth.php (17 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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 <[email protected]>
13
 * @author     Chris Smith <[email protected]>
14
 * @author     Matthias Grimm <[email protected]>
15
 * @author     Jan Schumann <[email protected]>
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 <[email protected]>
26
     * @author Andreas Gohr <[email protected]>
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']) ||
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
42
            empty($this->conf['password']) || empty($this->conf['database'])
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
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 <[email protected]>
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;
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
114
        }
115
        return true;
116
    }
117
118
    /**
119
     * Counts users which meet certain $filter criteria.
120
     *
121
     * @author  Matthias Grimm <[email protected]>
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);
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
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 <[email protected]>
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);
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
157
            $sql .= " ".$this->conf['SortOrder'];
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
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 <[email protected]>
186
     * @author Andreas Gohr   <[email protected]>
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']);
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
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'];
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
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']);
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
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 <[email protected]>
250
     * @author  Chris Smith <[email protected]>
251
     * @author  Matthias Grimm <[email protected]>
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']);
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
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 <[email protected]>
297
     *
298
     * @return bool
299
     */
300
    protected function _openDB() {
301
        if(!$this->dbcon) {
302
            $dsn = $this->conf['server'] ? 'host='.$this->conf['server'] : '';
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
303
            $dsn .= ' port='.$this->conf['port'];
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
304
            $dsn .= ' dbname='.$this->conf['database'];
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
305
            $dsn .= ' user='.$this->conf['user'];
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
306
            $dsn .= ' password='.$this->conf['password'];
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
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.",
0 ignored issues
show
The property conf cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
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 <[email protected]>
327
     */
328
    protected function _closeDB() {
329
        if($this->dbcon) {
330
            pg_close($this->dbcon);
331
            $this->dbcon = 0;
0 ignored issues
show
Documentation Bug introduced by
It seems like 0 of type integer is incompatible with the declared type resource of property $dbcon.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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
}