|
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']) || |
|
|
|
|
|
|
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 <[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; |
|
|
|
|
|
|
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); |
|
|
|
|
|
|
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); |
|
|
|
|
|
|
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 <[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']); |
|
|
|
|
|
|
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 <[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']); |
|
|
|
|
|
|
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'] : ''; |
|
|
|
|
|
|
303
|
|
|
$dsn .= ' port='.$this->conf['port']; |
|
|
|
|
|
|
304
|
|
|
$dsn .= ' dbname='.$this->conf['database']; |
|
|
|
|
|
|
305
|
|
|
$dsn .= ' user='.$this->conf['user']; |
|
|
|
|
|
|
306
|
|
|
$dsn .= ' password='.$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 <[email protected]> |
|
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 <[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
|
|
|
} |
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.