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