1 | <?php
|
---|
2 | /**
|
---|
3 | * MySQLP authentication backend
|
---|
4 | *
|
---|
5 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
---|
6 | * @author Andreas Gohr <[email protected]>
|
---|
7 | * @author Chris Smith <[email protected]>
|
---|
8 | * @author Matthias Grimm <[email protected]>
|
---|
9 | */
|
---|
10 |
|
---|
11 | class auth_mysql extends auth_basic {
|
---|
12 |
|
---|
13 | var $dbcon = 0;
|
---|
14 | var $dbver = 0; // database version
|
---|
15 | var $dbrev = 0; // database revision
|
---|
16 | var $dbsub = 0; // database subrevision
|
---|
17 | var $cnf = null;
|
---|
18 | var $defaultgroup = "";
|
---|
19 |
|
---|
20 | /**
|
---|
21 | * Constructor
|
---|
22 | *
|
---|
23 | * checks if the mysql interface is available, otherwise it will
|
---|
24 | * set the variable $success of the basis class to false
|
---|
25 | *
|
---|
26 | * @author Matthias Grimm <[email protected]>
|
---|
27 | */
|
---|
28 | function auth_mysql() {
|
---|
29 | global $conf;
|
---|
30 | $this->cnf = $conf['auth']['mysql'];
|
---|
31 |
|
---|
32 | if (method_exists($this, 'auth_basic'))
|
---|
33 | parent::auth_basic();
|
---|
34 |
|
---|
35 | if(!function_exists('mysql_connect')) {
|
---|
36 | if ($this->cnf['debug'])
|
---|
37 | msg("MySQL err: PHP MySQL extension not found.",-1,__LINE__,__FILE__);
|
---|
38 | $this->success = false;
|
---|
39 | return;
|
---|
40 | }
|
---|
41 |
|
---|
42 | // default to UTF-8, you rarely want something else
|
---|
43 | if(!isset($this->cnf['charset'])) $this->cnf['charset'] = 'utf8';
|
---|
44 |
|
---|
45 | $this->defaultgroup = $conf['defaultgroup'];
|
---|
46 |
|
---|
47 | // set capabilities based upon config strings set
|
---|
48 | if (empty($this->cnf['server']) || empty($this->cnf['user']) ||
|
---|
49 | !isset($this->cnf['password']) || empty($this->cnf['database'])){
|
---|
50 | if ($this->cnf['debug'])
|
---|
51 | msg("MySQL err: insufficient configuration.",-1,__LINE__,__FILE__);
|
---|
52 | $this->success = false;
|
---|
53 | return;
|
---|
54 | }
|
---|
55 |
|
---|
56 | $this->cando['addUser'] = $this->_chkcnf(array('getUserInfo',
|
---|
57 | 'getGroups',
|
---|
58 | 'addUser',
|
---|
59 | 'getUserID',
|
---|
60 | 'getGroupID',
|
---|
61 | 'addGroup',
|
---|
62 | 'addUserGroup'),true);
|
---|
63 | $this->cando['delUser'] = $this->_chkcnf(array('getUserID',
|
---|
64 | 'delUser',
|
---|
65 | 'delUserRefs'),true);
|
---|
66 | $this->cando['modLogin'] = $this->_chkcnf(array('getUserID',
|
---|
67 | 'updateUser',
|
---|
68 | 'UpdateTarget'),true);
|
---|
69 | $this->cando['modPass'] = $this->cando['modLogin'];
|
---|
70 | $this->cando['modName'] = $this->cando['modLogin'];
|
---|
71 | $this->cando['modMail'] = $this->cando['modLogin'];
|
---|
72 | $this->cando['modGroups'] = $this->_chkcnf(array('getUserID',
|
---|
73 | 'getGroups',
|
---|
74 | 'getGroupID',
|
---|
75 | 'addGroup',
|
---|
76 | 'addUserGroup',
|
---|
77 | 'delGroup',
|
---|
78 | 'getGroupID',
|
---|
79 | 'delUserGroup'),true);
|
---|
80 | /* getGroups is not yet supported
|
---|
81 | $this->cando['getGroups'] = $this->_chkcnf(array('getGroups',
|
---|
82 | 'getGroupID'),false); */
|
---|
83 | $this->cando['getUsers'] = $this->_chkcnf(array('getUsers',
|
---|
84 | 'getUserInfo',
|
---|
85 | 'getGroups'),false);
|
---|
86 | $this->cando['getUserCount'] = $this->_chkcnf(array('getUsers'),false);
|
---|
87 | }
|
---|
88 |
|
---|
89 | /**
|
---|
90 | * Check if the given config strings are set
|
---|
91 | *
|
---|
92 | * @author Matthias Grimm <[email protected]>
|
---|
93 | * @return bool
|
---|
94 | */
|
---|
95 | function _chkcnf($keys, $wop=false){
|
---|
96 | foreach ($keys as $key){
|
---|
97 | if (empty($this->cnf[$key])) return false;
|
---|
98 | }
|
---|
99 |
|
---|
100 | /* write operation and lock array filled with tables names? */
|
---|
101 | if ($wop && (!is_array($this->cnf['TablesToLock']) ||
|
---|
102 | !count($this->cnf['TablesToLock']))){
|
---|
103 | return false;
|
---|
104 | }
|
---|
105 |
|
---|
106 | return true;
|
---|
107 | }
|
---|
108 |
|
---|
109 | /**
|
---|
110 | * Checks if the given user exists and the given plaintext password
|
---|
111 | * is correct. Furtheron it might be checked wether the user is
|
---|
112 | * member of the right group
|
---|
113 | *
|
---|
114 | * Depending on which SQL string is defined in the config, password
|
---|
115 | * checking is done here (getpass) or by the database (passcheck)
|
---|
116 | *
|
---|
117 | * @param $user user who would like access
|
---|
118 | * @param $pass user's clear text password to check
|
---|
119 | * @return bool
|
---|
120 | *
|
---|
121 | * @author Andreas Gohr <[email protected]>
|
---|
122 | * @author Matthias Grimm <[email protected]>
|
---|
123 | */
|
---|
124 | function checkPass($user,$pass){
|
---|
125 | $rc = false;
|
---|
126 |
|
---|
127 | if($this->_openDB()) {
|
---|
128 | $sql = str_replace('%{user}',$this->_escape($user),$this->cnf['checkPass']);
|
---|
129 | $sql = str_replace('%{pass}',$this->_escape($pass),$sql);
|
---|
130 | $sql = str_replace('%{dgroup}',$this->_escape($this->defaultgroup),$sql);
|
---|
131 | $result = $this->_queryDB($sql);
|
---|
132 |
|
---|
133 | if($result !== false && count($result) == 1) {
|
---|
134 | if($this->cnf['forwardClearPass'] == 1)
|
---|
135 | $rc = true;
|
---|
136 | else
|
---|
137 | $rc = auth_verifyPassword($pass,$result[0]['pass']);
|
---|
138 | }
|
---|
139 | $this->_closeDB();
|
---|
140 | }
|
---|
141 | return $rc;
|
---|
142 | }
|
---|
143 |
|
---|
144 | /**
|
---|
145 | * [public function]
|
---|
146 | *
|
---|
147 | * Returns info about the given user needs to contain
|
---|
148 | * at least these fields:
|
---|
149 | * name string full name of the user
|
---|
150 | * mail string email addres of the user
|
---|
151 | * grps array list of groups the user is in
|
---|
152 | *
|
---|
153 | * @param $user user's nick to get data for
|
---|
154 | *
|
---|
155 | * @author Andreas Gohr <[email protected]>
|
---|
156 | * @author Matthias Grimm <[email protected]>
|
---|
157 | */
|
---|
158 | function getUserData($user){
|
---|
159 | if($this->_openDB()) {
|
---|
160 | $this->_lockTables("READ");
|
---|
161 | $info = $this->_getUserInfo($user);
|
---|
162 | $this->_unlockTables();
|
---|
163 | $this->_closeDB();
|
---|
164 | } else
|
---|
165 | $info = false;
|
---|
166 | return $info;
|
---|
167 | }
|
---|
168 |
|
---|
169 | /**
|
---|
170 | * [public function]
|
---|
171 | *
|
---|
172 | * Create a new User. Returns false if the user already exists,
|
---|
173 | * null when an error occurred and true if everything went well.
|
---|
174 | *
|
---|
175 | * The new user will be added to the default group by this
|
---|
176 | * function if grps are not specified (default behaviour).
|
---|
177 | *
|
---|
178 | * @param $user nick of the user
|
---|
179 | * @param $pwd clear text password
|
---|
180 | * @param $name full name of the user
|
---|
181 | * @param $mail email address
|
---|
182 | * @param $grps array of groups the user should become member of
|
---|
183 | *
|
---|
184 | * @author Andreas Gohr <[email protected]>
|
---|
185 | * @author Chris Smith <[email protected]>
|
---|
186 | * @author Matthias Grimm <[email protected]>
|
---|
187 | */
|
---|
188 | function createUser($user,$pwd,$name,$mail,$grps=null){
|
---|
189 | if($this->_openDB()) {
|
---|
190 | if (($info = $this->_getUserInfo($user)) !== false)
|
---|
191 | return false; // user already exists
|
---|
192 |
|
---|
193 | // set defaultgroup if no groups were given
|
---|
194 | if ($grps == null)
|
---|
195 | $grps = array($this->defaultgroup);
|
---|
196 |
|
---|
197 | $this->_lockTables("WRITE");
|
---|
198 | $pwd = $this->cnf['forwardClearPass'] ? $pwd : auth_cryptPassword($pwd);
|
---|
199 | $rc = $this->_addUser($user,$pwd,$name,$mail,$grps);
|
---|
200 | $this->_unlockTables();
|
---|
201 | $this->_closeDB();
|
---|
202 | if ($rc) return true;
|
---|
203 | }
|
---|
204 | return null; // return error
|
---|
205 | }
|
---|
206 |
|
---|
207 | /**
|
---|
208 | * Modify user data [public function]
|
---|
209 | *
|
---|
210 | * An existing user dataset will be modified. Changes are given in an array.
|
---|
211 | *
|
---|
212 | * The dataset update will be rejected if the user name should be changed
|
---|
213 | * to an already existing one.
|
---|
214 | *
|
---|
215 | * The password must be provides unencrypted. Pasword cryption is done
|
---|
216 | * automatically if configured.
|
---|
217 | *
|
---|
218 | * If one or more groups could't be updated, an error would be set. In
|
---|
219 | * this case the dataset might already be changed and we can't rollback
|
---|
220 | * the changes. Transactions would be really usefull here.
|
---|
221 | *
|
---|
222 | * modifyUser() may be called without SQL statements defined that are
|
---|
223 | * needed to change group membership (for example if only the user profile
|
---|
224 | * should be modified). In this case we asure that we don't touch groups
|
---|
225 | * even $changes['grps'] is set by mistake.
|
---|
226 | *
|
---|
227 | * @param $user nick of the user to be changed
|
---|
228 | * @param $changes array of field/value pairs to be changed (password
|
---|
229 | * will be clear text)
|
---|
230 | * @return bool true on success, false on error
|
---|
231 | *
|
---|
232 | * @author Chris Smith <[email protected]>
|
---|
233 | * @author Matthias Grimm <[email protected]>
|
---|
234 | */
|
---|
235 | function modifyUser($user, $changes) {
|
---|
236 | $rc = false;
|
---|
237 |
|
---|
238 | if (!is_array($changes) || !count($changes))
|
---|
239 | return true; // nothing to change
|
---|
240 |
|
---|
241 | if($this->_openDB()) {
|
---|
242 | $this->_lockTables("WRITE");
|
---|
243 |
|
---|
244 | if (($uid = $this->_getUserID($user))) {
|
---|
245 | $rc = $this->_updateUserInfo($changes, $uid);
|
---|
246 |
|
---|
247 | if ($rc && isset($changes['grps']) && $this->cando['modGroups']) {
|
---|
248 | $groups = $this->_getGroups($user);
|
---|
249 | $grpadd = array_diff($changes['grps'], $groups);
|
---|
250 | $grpdel = array_diff($groups, $changes['grps']);
|
---|
251 |
|
---|
252 | foreach($grpadd as $group)
|
---|
253 | if (($this->_addUserToGroup($user, $group, 1)) == false)
|
---|
254 | $rc = false;
|
---|
255 |
|
---|
256 | foreach($grpdel as $group)
|
---|
257 | if (($this->_delUserFromGroup($user, $group)) == false)
|
---|
258 | $rc = false;
|
---|
259 | }
|
---|
260 | }
|
---|
261 |
|
---|
262 | $this->_unlockTables();
|
---|
263 | $this->_closeDB();
|
---|
264 | }
|
---|
265 | return $rc;
|
---|
266 | }
|
---|
267 |
|
---|
268 | /**
|
---|
269 | * [public function]
|
---|
270 | *
|
---|
271 | * Remove one or more users from the list of registered users
|
---|
272 | *
|
---|
273 | * @param array $users array of users to be deleted
|
---|
274 | * @return int the number of users deleted
|
---|
275 | *
|
---|
276 | * @author Christopher Smith <[email protected]>
|
---|
277 | * @author Matthias Grimm <[email protected]>
|
---|
278 | */
|
---|
279 | function deleteUsers($users) {
|
---|
280 | $count = 0;
|
---|
281 |
|
---|
282 | if($this->_openDB()) {
|
---|
283 | if (is_array($users) && count($users)) {
|
---|
284 | $this->_lockTables("WRITE");
|
---|
285 | foreach ($users as $user) {
|
---|
286 | if ($this->_delUser($user))
|
---|
287 | $count++;
|
---|
288 | }
|
---|
289 | $this->_unlockTables();
|
---|
290 | }
|
---|
291 | $this->_closeDB();
|
---|
292 | }
|
---|
293 | return $count;
|
---|
294 | }
|
---|
295 |
|
---|
296 | /**
|
---|
297 | * [public function]
|
---|
298 | *
|
---|
299 | * Counts users which meet certain $filter criteria.
|
---|
300 | *
|
---|
301 | * @param array $filter filter criteria in item/pattern pairs
|
---|
302 | * @return count of found users.
|
---|
303 | *
|
---|
304 | * @author Matthias Grimm <[email protected]>
|
---|
305 | */
|
---|
306 | function getUserCount($filter=array()) {
|
---|
307 | $rc = 0;
|
---|
308 |
|
---|
309 | if($this->_openDB()) {
|
---|
310 | $sql = $this->_createSQLFilter($this->cnf['getUsers'], $filter);
|
---|
311 |
|
---|
312 | if ($this->dbver >= 4) {
|
---|
313 | $sql = substr($sql, 6); /* remove 'SELECT' or 'select' */
|
---|
314 | $sql = "SELECT SQL_CALC_FOUND_ROWS".$sql." LIMIT 1";
|
---|
315 | $this->_queryDB($sql);
|
---|
316 | $result = $this->_queryDB("SELECT FOUND_ROWS()");
|
---|
317 | $rc = $result[0]['FOUND_ROWS()'];
|
---|
318 | } else if (($result = $this->_queryDB($sql)))
|
---|
319 | $rc = count($result);
|
---|
320 |
|
---|
321 | $this->_closeDB();
|
---|
322 | }
|
---|
323 | return $rc;
|
---|
324 | }
|
---|
325 |
|
---|
326 | /**
|
---|
327 | * Bulk retrieval of user data. [public function]
|
---|
328 | *
|
---|
329 | * @param first index of first user to be returned
|
---|
330 | * @param limit max number of users to be returned
|
---|
331 | * @param filter array of field/pattern pairs
|
---|
332 | * @return array of userinfo (refer getUserData for internal userinfo details)
|
---|
333 | *
|
---|
334 | * @author Matthias Grimm <[email protected]>
|
---|
335 | */
|
---|
336 | function retrieveUsers($first=0,$limit=10,$filter=array()) {
|
---|
337 | $out = array();
|
---|
338 |
|
---|
339 | if($this->_openDB()) {
|
---|
340 | $this->_lockTables("READ");
|
---|
341 | $sql = $this->_createSQLFilter($this->cnf['getUsers'], $filter);
|
---|
342 | $sql .= " ".$this->cnf['SortOrder']." LIMIT $first, $limit";
|
---|
343 | $result = $this->_queryDB($sql);
|
---|
344 |
|
---|
345 | if (!empty($result)) {
|
---|
346 | foreach ($result as $user)
|
---|
347 | if (($info = $this->_getUserInfo($user['user'])))
|
---|
348 | $out[$user['user']] = $info;
|
---|
349 | }
|
---|
350 |
|
---|
351 | $this->_unlockTables();
|
---|
352 | $this->_closeDB();
|
---|
353 | }
|
---|
354 | return $out;
|
---|
355 | }
|
---|
356 |
|
---|
357 | /**
|
---|
358 | * Give user membership of a group [public function]
|
---|
359 | *
|
---|
360 | * @param $user
|
---|
361 | * @param $group
|
---|
362 | * @return bool true on success, false on error
|
---|
363 | *
|
---|
364 | * @author Matthias Grimm <[email protected]>
|
---|
365 | */
|
---|
366 | function joinGroup($user, $group) {
|
---|
367 | $rc = false;
|
---|
368 |
|
---|
369 | if ($this->_openDB()) {
|
---|
370 | $this->_lockTables("WRITE");
|
---|
371 | $rc = $this->_addUserToGroup($user, $group);
|
---|
372 | $this->_unlockTables();
|
---|
373 | $this->_closeDB();
|
---|
374 | }
|
---|
375 | return $rc;
|
---|
376 | }
|
---|
377 |
|
---|
378 | /**
|
---|
379 | * Remove user from a group [public function]
|
---|
380 | *
|
---|
381 | * @param $user user that leaves a group
|
---|
382 | * @param $group group to leave
|
---|
383 | * @return bool
|
---|
384 | *
|
---|
385 | * @author Matthias Grimm <[email protected]>
|
---|
386 | */
|
---|
387 | function leaveGroup($user, $group) {
|
---|
388 | $rc = false;
|
---|
389 |
|
---|
390 | if ($this->_openDB()) {
|
---|
391 | $this->_lockTables("WRITE");
|
---|
392 | $uid = $this->_getUserID($user);
|
---|
393 | $rc = $this->_delUserFromGroup($user, $group);
|
---|
394 | $this->_unlockTables();
|
---|
395 | $this->_closeDB();
|
---|
396 | }
|
---|
397 | return $rc;
|
---|
398 | }
|
---|
399 |
|
---|
400 | /**
|
---|
401 | * MySQL is case-insensitive
|
---|
402 | */
|
---|
403 | function isCaseSensitive(){
|
---|
404 | return false;
|
---|
405 | }
|
---|
406 |
|
---|
407 | /**
|
---|
408 | * Adds a user to a group.
|
---|
409 | *
|
---|
410 | * If $force is set to '1' non existing groups would be created.
|
---|
411 | *
|
---|
412 | * The database connection must already be established. Otherwise
|
---|
413 | * this function does nothing and returns 'false'. It is strongly
|
---|
414 | * recommended to call this function only after all participating
|
---|
415 | * tables (group and usergroup) have been locked.
|
---|
416 | *
|
---|
417 | * @param $user user to add to a group
|
---|
418 | * @param $group name of the group
|
---|
419 | * @param $force '1' create missing groups
|
---|
420 | * @return bool 'true' on success, 'false' on error
|
---|
421 | *
|
---|
422 | * @author Matthias Grimm <[email protected]>
|
---|
423 | */
|
---|
424 | function _addUserToGroup($user, $group, $force=0) {
|
---|
425 | $newgroup = 0;
|
---|
426 |
|
---|
427 | if (($this->dbcon) && ($user)) {
|
---|
428 | $gid = $this->_getGroupID($group);
|
---|
429 | if (!$gid) {
|
---|
430 | if ($force) { // create missing groups
|
---|
431 | $sql = str_replace('%{group}',$this->_escape($group),$this->cnf['addGroup']);
|
---|
432 | $gid = $this->_modifyDB($sql);
|
---|
433 | $newgroup = 1; // group newly created
|
---|
434 | }
|
---|
435 | if (!$gid) return false; // group didn't exist and can't be created
|
---|
436 | }
|
---|
437 |
|
---|
438 | $sql = $this->cnf['addUserGroup'];
|
---|
439 | if(strpos($sql,'%{uid}') !== false){
|
---|
440 | $uid = $this->_getUserID($user);
|
---|
441 | $sql = str_replace('%{uid}', $this->_escape($uid),$sql);
|
---|
442 | }
|
---|
443 | $sql = str_replace('%{user}', $this->_escape($user),$sql);
|
---|
444 | $sql = str_replace('%{gid}', $this->_escape($gid),$sql);
|
---|
445 | $sql = str_replace('%{group}',$this->_escape($group),$sql);
|
---|
446 | if ($this->_modifyDB($sql) !== false) return true;
|
---|
447 |
|
---|
448 | if ($newgroup) { // remove previously created group on error
|
---|
449 | $sql = str_replace('%{gid}', $this->_escape($gid),$this->cnf['delGroup']);
|
---|
450 | $sql = str_replace('%{group}',$this->_escape($group),$sql);
|
---|
451 | $this->_modifyDB($sql);
|
---|
452 | }
|
---|
453 | }
|
---|
454 | return false;
|
---|
455 | }
|
---|
456 |
|
---|
457 | /**
|
---|
458 | * Remove user from a group
|
---|
459 | *
|
---|
460 | * @param $user user that leaves a group
|
---|
461 | * @param $group group to leave
|
---|
462 | * @return bool true on success, false on error
|
---|
463 | *
|
---|
464 | * @author Matthias Grimm <[email protected]>
|
---|
465 | */
|
---|
466 | function _delUserFromGroup($user, $group) {
|
---|
467 | $rc = false;
|
---|
468 |
|
---|
469 |
|
---|
470 | if (($this->dbcon) && ($user)) {
|
---|
471 | $sql = $this->cnf['delUserGroup'];
|
---|
472 | if(strpos($sql,'%{uid}') !== false){
|
---|
473 | $uid = $this->_getUserID($user);
|
---|
474 | $sql = str_replace('%{uid}', $this->_escape($uid),$sql);
|
---|
475 | }
|
---|
476 | $gid = $this->_getGroupID($group);
|
---|
477 | if ($gid) {
|
---|
478 | $sql = str_replace('%{user}', $this->_escape($user),$sql);
|
---|
479 | $sql = str_replace('%{gid}', $this->_escape($gid),$sql);
|
---|
480 | $sql = str_replace('%{group}',$this->_escape($group),$sql);
|
---|
481 | $rc = $this->_modifyDB($sql) == 0 ? true : false;
|
---|
482 | }
|
---|
483 | }
|
---|
484 | return $rc;
|
---|
485 | }
|
---|
486 |
|
---|
487 | /**
|
---|
488 | * Retrieves a list of groups the user is a member off.
|
---|
489 | *
|
---|
490 | * The database connection must already be established
|
---|
491 | * for this function to work. Otherwise it will return
|
---|
492 | * 'false'.
|
---|
493 | *
|
---|
494 | * @param $user user whose groups should be listed
|
---|
495 | * @return bool false on error
|
---|
496 | * @return array array containing all groups on success
|
---|
497 | *
|
---|
498 | * @author Matthias Grimm <[email protected]>
|
---|
499 | */
|
---|
500 | function _getGroups($user) {
|
---|
501 | $groups = array();
|
---|
502 |
|
---|
503 | if($this->dbcon) {
|
---|
504 | $sql = str_replace('%{user}',$this->_escape($user),$this->cnf['getGroups']);
|
---|
505 | $result = $this->_queryDB($sql);
|
---|
506 |
|
---|
507 | if($result !== false && count($result)) {
|
---|
508 | foreach($result as $row)
|
---|
509 | $groups[] = $row['group'];
|
---|
510 | }
|
---|
511 | return $groups;
|
---|
512 | }
|
---|
513 | return false;
|
---|
514 | }
|
---|
515 |
|
---|
516 | /**
|
---|
517 | * Retrieves the user id of a given user name
|
---|
518 | *
|
---|
519 | * The database connection must already be established
|
---|
520 | * for this function to work. Otherwise it will return
|
---|
521 | * 'false'.
|
---|
522 | *
|
---|
523 | * @param $user user whose id is desired
|
---|
524 | * @return user id
|
---|
525 | *
|
---|
526 | * @author Matthias Grimm <[email protected]>
|
---|
527 | */
|
---|
528 | function _getUserID($user) {
|
---|
529 | if($this->dbcon) {
|
---|
530 | $sql = str_replace('%{user}',$this->_escape($user),$this->cnf['getUserID']);
|
---|
531 | $result = $this->_queryDB($sql);
|
---|
532 | return $result === false ? false : $result[0]['id'];
|
---|
533 | }
|
---|
534 | return false;
|
---|
535 | }
|
---|
536 |
|
---|
537 | /**
|
---|
538 | * Adds a new User to the database.
|
---|
539 | *
|
---|
540 | * The database connection must already be established
|
---|
541 | * for this function to work. Otherwise it will return
|
---|
542 | * 'false'.
|
---|
543 | *
|
---|
544 | * @param $user login of the user
|
---|
545 | * @param $pwd encrypted password
|
---|
546 | * @param $name full name of the user
|
---|
547 | * @param $mail email address
|
---|
548 | * @param $grps array of groups the user should become member of
|
---|
549 | * @return bool
|
---|
550 | *
|
---|
551 | * @author Andreas Gohr <[email protected]>
|
---|
552 | * @author Chris Smith <[email protected]>
|
---|
553 | * @author Matthias Grimm <[email protected]>
|
---|
554 | */
|
---|
555 | function _addUser($user,$pwd,$name,$mail,$grps){
|
---|
556 | if($this->dbcon && is_array($grps)) {
|
---|
557 | $sql = str_replace('%{user}', $this->_escape($user),$this->cnf['addUser']);
|
---|
558 | $sql = str_replace('%{pass}', $this->_escape($pwd),$sql);
|
---|
559 | $sql = str_replace('%{name}', $this->_escape($name),$sql);
|
---|
560 | $sql = str_replace('%{email}',$this->_escape($mail),$sql);
|
---|
561 | $uid = $this->_modifyDB($sql);
|
---|
562 |
|
---|
563 | if ($uid) {
|
---|
564 | foreach($grps as $group) {
|
---|
565 | $gid = $this->_addUserToGroup($user, $group, 1);
|
---|
566 | if ($gid === false) break;
|
---|
567 | }
|
---|
568 |
|
---|
569 | if ($gid) return true;
|
---|
570 | else {
|
---|
571 | /* remove the new user and all group relations if a group can't
|
---|
572 | * be assigned. Newly created groups will remain in the database
|
---|
573 | * and won't be removed. This might create orphaned groups but
|
---|
574 | * is not a big issue so we ignore this problem here.
|
---|
575 | */
|
---|
576 | $this->_delUser($user);
|
---|
577 | if ($this->cnf['debug'])
|
---|
578 | msg ("MySQL err: Adding user '$user' to group '$group' failed.",-1,__LINE__,__FILE__);
|
---|
579 | }
|
---|
580 | }
|
---|
581 | }
|
---|
582 | return false;
|
---|
583 | }
|
---|
584 |
|
---|
585 | /**
|
---|
586 | * Deletes a given user and all his group references.
|
---|
587 | *
|
---|
588 | * The database connection must already be established
|
---|
589 | * for this function to work. Otherwise it will return
|
---|
590 | * 'false'.
|
---|
591 | *
|
---|
592 | * @param $user user whose id is desired
|
---|
593 | * @return bool
|
---|
594 | *
|
---|
595 | * @author Matthias Grimm <[email protected]>
|
---|
596 | */
|
---|
597 | function _delUser($user) {
|
---|
598 | if($this->dbcon) {
|
---|
599 | $uid = $this->_getUserID($user);
|
---|
600 | if ($uid) {
|
---|
601 | $sql = str_replace('%{uid}',$this->_escape($uid),$this->cnf['delUserRefs']);
|
---|
602 | $this->_modifyDB($sql);
|
---|
603 | $sql = str_replace('%{uid}',$this->_escape($uid),$this->cnf['delUser']);
|
---|
604 | $sql = str_replace('%{user}', $this->_escape($user),$sql);
|
---|
605 | $this->_modifyDB($sql);
|
---|
606 | return true;
|
---|
607 | }
|
---|
608 | }
|
---|
609 | return false;
|
---|
610 | }
|
---|
611 |
|
---|
612 | /**
|
---|
613 | * getUserInfo
|
---|
614 | *
|
---|
615 | * Gets the data for a specific user The database connection
|
---|
616 | * must already be established for this function to work.
|
---|
617 | * Otherwise it will return 'false'.
|
---|
618 | *
|
---|
619 | * @param $user user's nick to get data for
|
---|
620 | * @return bool false on error
|
---|
621 | * @return array user info on success
|
---|
622 | *
|
---|
623 | * @author Matthias Grimm <[email protected]>
|
---|
624 | */
|
---|
625 | function _getUserInfo($user){
|
---|
626 | $sql = str_replace('%{user}',$this->_escape($user),$this->cnf['getUserInfo']);
|
---|
627 | $result = $this->_queryDB($sql);
|
---|
628 | if($result !== false && count($result)) {
|
---|
629 | $info = $result[0];
|
---|
630 | $info['grps'] = $this->_getGroups($user);
|
---|
631 | return $info;
|
---|
632 | }
|
---|
633 | return false;
|
---|
634 | }
|
---|
635 |
|
---|
636 | /**
|
---|
637 | * Updates the user info in the database
|
---|
638 | *
|
---|
639 | * Update a user data structure in the database according changes
|
---|
640 | * given in an array. The user name can only be changes if it didn't
|
---|
641 | * exists already. If the new user name exists the update procedure
|
---|
642 | * will be aborted. The database keeps unchanged.
|
---|
643 | *
|
---|
644 | * The database connection has already to be established for this
|
---|
645 | * function to work. Otherwise it will return 'false'.
|
---|
646 | *
|
---|
647 | * The password will be crypted if necessary.
|
---|
648 | *
|
---|
649 | * @param $changes array of items to change as pairs of item and value
|
---|
650 | * @param $uid user id of dataset to change, must be unique in DB
|
---|
651 | * @return true on success or false on error
|
---|
652 | *
|
---|
653 | * @author Matthias Grimm <[email protected]>
|
---|
654 | */
|
---|
655 | function _updateUserInfo($changes, $uid) {
|
---|
656 | $sql = $this->cnf['updateUser']." ";
|
---|
657 | $cnt = 0;
|
---|
658 | $err = 0;
|
---|
659 |
|
---|
660 | if($this->dbcon) {
|
---|
661 | foreach ($changes as $item => $value) {
|
---|
662 | if ($item == 'user') {
|
---|
663 | if (($this->_getUserID($changes['user']))) {
|
---|
664 | $err = 1; /* new username already exists */
|
---|
665 | break; /* abort update */
|
---|
666 | }
|
---|
667 | if ($cnt++ > 0) $sql .= ", ";
|
---|
668 | $sql .= str_replace('%{user}',$value,$this->cnf['UpdateLogin']);
|
---|
669 | } else if ($item == 'name') {
|
---|
670 | if ($cnt++ > 0) $sql .= ", ";
|
---|
671 | $sql .= str_replace('%{name}',$value,$this->cnf['UpdateName']);
|
---|
672 | } else if ($item == 'pass') {
|
---|
673 | if (!$this->cnf['forwardClearPass'])
|
---|
674 | $value = auth_cryptPassword($value);
|
---|
675 | if ($cnt++ > 0) $sql .= ", ";
|
---|
676 | $sql .= str_replace('%{pass}',$value,$this->cnf['UpdatePass']);
|
---|
677 | } else if ($item == 'mail') {
|
---|
678 | if ($cnt++ > 0) $sql .= ", ";
|
---|
679 | $sql .= str_replace('%{email}',$value,$this->cnf['UpdateEmail']);
|
---|
680 | }
|
---|
681 | }
|
---|
682 |
|
---|
683 | if ($err == 0) {
|
---|
684 | if ($cnt > 0) {
|
---|
685 | $sql .= " ".str_replace('%{uid}', $uid, $this->cnf['UpdateTarget']);
|
---|
686 | if(get_class($this) == 'auth_mysql') $sql .= " LIMIT 1"; //some PgSQL inheritance comp.
|
---|
687 | $this->_modifyDB($sql);
|
---|
688 | }
|
---|
689 | return true;
|
---|
690 | }
|
---|
691 | }
|
---|
692 | return false;
|
---|
693 | }
|
---|
694 |
|
---|
695 | /**
|
---|
696 | * Retrieves the group id of a given group name
|
---|
697 | *
|
---|
698 | * The database connection must already be established
|
---|
699 | * for this function to work. Otherwise it will return
|
---|
700 | * 'false'.
|
---|
701 | *
|
---|
702 | * @param $group group name which id is desired
|
---|
703 | * @return group id
|
---|
704 | *
|
---|
705 | * @author Matthias Grimm <[email protected]>
|
---|
706 | */
|
---|
707 | function _getGroupID($group) {
|
---|
708 | if($this->dbcon) {
|
---|
709 | $sql = str_replace('%{group}',$this->_escape($group),$this->cnf['getGroupID']);
|
---|
710 | $result = $this->_queryDB($sql);
|
---|
711 | return $result === false ? false : $result[0]['id'];
|
---|
712 | }
|
---|
713 | return false;
|
---|
714 | }
|
---|
715 |
|
---|
716 | /**
|
---|
717 | * Opens a connection to a database and saves the handle for further
|
---|
718 | * usage in the object. The successful call to this functions is
|
---|
719 | * essential for most functions in this object.
|
---|
720 | *
|
---|
721 | * @return bool
|
---|
722 | *
|
---|
723 | * @author Matthias Grimm <[email protected]>
|
---|
724 | */
|
---|
725 | function _openDB() {
|
---|
726 | if (!$this->dbcon) {
|
---|
727 | $con = @mysql_connect ($this->cnf['server'], $this->cnf['user'], $this->cnf['password']);
|
---|
728 | if ($con) {
|
---|
729 | if ((mysql_select_db($this->cnf['database'], $con))) {
|
---|
730 | if ((preg_match("/^(\d+)\.(\d+)\.(\d+).*/", mysql_get_server_info ($con), $result)) == 1) {
|
---|
731 | $this->dbver = $result[1];
|
---|
732 | $this->dbrev = $result[2];
|
---|
733 | $this->dbsub = $result[3];
|
---|
734 | }
|
---|
735 | $this->dbcon = $con;
|
---|
736 | if(!empty($this->cnf['charset'])){
|
---|
737 | mysql_query('SET CHARACTER SET "' . $this->cnf['charset'] . '"', $con);
|
---|
738 | }
|
---|
739 | return true; // connection and database successfully opened
|
---|
740 | } else {
|
---|
741 | mysql_close ($con);
|
---|
742 | if ($this->cnf['debug'])
|
---|
743 | msg("MySQL err: No access to database {$this->cnf['database']}.",-1,__LINE__,__FILE__);
|
---|
744 | }
|
---|
745 | } else if ($this->cnf['debug'])
|
---|
746 | msg ("MySQL err: Connection to {$this->cnf['user']}@{$this->cnf['server']} not possible.",
|
---|
747 | -1,__LINE__,__FILE__);
|
---|
748 |
|
---|
749 | return false; // connection failed
|
---|
750 | }
|
---|
751 | return true; // connection already open
|
---|
752 | }
|
---|
753 |
|
---|
754 | /**
|
---|
755 | * Closes a database connection.
|
---|
756 | *
|
---|
757 | * @author Matthias Grimm <[email protected]>
|
---|
758 | */
|
---|
759 | function _closeDB() {
|
---|
760 | if ($this->dbcon) {
|
---|
761 | mysql_close ($this->dbcon);
|
---|
762 | $this->dbcon = 0;
|
---|
763 | }
|
---|
764 | }
|
---|
765 |
|
---|
766 | /**
|
---|
767 | * Sends a SQL query to the database and transforms the result into
|
---|
768 | * an associative array.
|
---|
769 | *
|
---|
770 | * This function is only able to handle queries that returns a
|
---|
771 | * table such as SELECT.
|
---|
772 | *
|
---|
773 | * @param $query SQL string that contains the query
|
---|
774 | * @return array with the result table
|
---|
775 | *
|
---|
776 | * @author Matthias Grimm <[email protected]>
|
---|
777 | */
|
---|
778 | function _queryDB($query) {
|
---|
779 | if($this->cnf['debug'] >= 2){
|
---|
780 | msg('MySQL query: '.hsc($query),0,__LINE__,__FILE__);
|
---|
781 | }
|
---|
782 |
|
---|
783 | $resultarray = array();
|
---|
784 | if ($this->dbcon) {
|
---|
785 | $result = @mysql_query($query,$this->dbcon);
|
---|
786 | if ($result) {
|
---|
787 | while (($t = mysql_fetch_assoc($result)) !== false)
|
---|
788 | $resultarray[]=$t;
|
---|
789 | mysql_free_result ($result);
|
---|
790 | return $resultarray;
|
---|
791 | }
|
---|
792 | if ($this->cnf['debug'])
|
---|
793 | msg('MySQL err: '.mysql_error($this->dbcon),-1,__LINE__,__FILE__);
|
---|
794 | }
|
---|
795 | return false;
|
---|
796 | }
|
---|
797 |
|
---|
798 | /**
|
---|
799 | * Sends a SQL query to the database
|
---|
800 | *
|
---|
801 | * This function is only able to handle queries that returns
|
---|
802 | * either nothing or an id value such as INPUT, DELETE, UPDATE, etc.
|
---|
803 | *
|
---|
804 | * @param $query SQL string that contains the query
|
---|
805 | * @return insert id or 0, false on error
|
---|
806 | *
|
---|
807 | * @author Matthias Grimm <[email protected]>
|
---|
808 | */
|
---|
809 | function _modifyDB($query) {
|
---|
810 | if ($this->dbcon) {
|
---|
811 | $result = @mysql_query($query,$this->dbcon);
|
---|
812 | if ($result) {
|
---|
813 | $rc = mysql_insert_id($this->dbcon); //give back ID on insert
|
---|
814 | if ($rc !== false) return $rc;
|
---|
815 | }
|
---|
816 | if ($this->cnf['debug'])
|
---|
817 | msg('MySQL err: '.mysql_error($this->dbcon),-1,__LINE__,__FILE__);
|
---|
818 | }
|
---|
819 | return false;
|
---|
820 | }
|
---|
821 |
|
---|
822 | /**
|
---|
823 | * Locked a list of tables for exclusive access so that modifications
|
---|
824 | * to the database can't be disturbed by other threads. The list
|
---|
825 | * could be set with $conf['auth']['mysql']['TablesToLock'] = array()
|
---|
826 | *
|
---|
827 | * If aliases for tables are used in SQL statements, also this aliases
|
---|
828 | * must be locked. For eg. you use a table 'user' and the alias 'u' in
|
---|
829 | * some sql queries, the array must looks like this (order is important):
|
---|
830 | * array("user", "user AS u");
|
---|
831 | *
|
---|
832 | * MySQL V3 is not able to handle transactions with COMMIT/ROLLBACK
|
---|
833 | * so that this functionality is simulated by this function. Nevertheless
|
---|
834 | * it is not as powerful as transactions, it is a good compromise in safty.
|
---|
835 | *
|
---|
836 | * @param $mode could be 'READ' or 'WRITE'
|
---|
837 | *
|
---|
838 | * @author Matthias Grimm <[email protected]>
|
---|
839 | */
|
---|
840 | function _lockTables($mode) {
|
---|
841 | if ($this->dbcon) {
|
---|
842 | if (is_array($this->cnf['TablesToLock']) && !empty($this->cnf['TablesToLock'])) {
|
---|
843 | if ($mode == "READ" || $mode == "WRITE") {
|
---|
844 | $sql = "LOCK TABLES ";
|
---|
845 | $cnt = 0;
|
---|
846 | foreach ($this->cnf['TablesToLock'] as $table) {
|
---|
847 | if ($cnt++ != 0) $sql .= ", ";
|
---|
848 | $sql .= "$table $mode";
|
---|
849 | }
|
---|
850 | $this->_modifyDB($sql);
|
---|
851 | return true;
|
---|
852 | }
|
---|
853 | }
|
---|
854 | }
|
---|
855 | return false;
|
---|
856 | }
|
---|
857 |
|
---|
858 | /**
|
---|
859 | * Unlock locked tables. All existing locks of this thread will be
|
---|
860 | * abrogated.
|
---|
861 | *
|
---|
862 | * @author Matthias Grimm <[email protected]>
|
---|
863 | */
|
---|
864 | function _unlockTables() {
|
---|
865 | if ($this->dbcon) {
|
---|
866 | $this->_modifyDB("UNLOCK TABLES");
|
---|
867 | return true;
|
---|
868 | }
|
---|
869 | return false;
|
---|
870 | }
|
---|
871 |
|
---|
872 | /**
|
---|
873 | * Transforms the filter settings in an filter string for a SQL database
|
---|
874 | * The database connection must already be established, otherwise the
|
---|
875 | * original SQL string without filter criteria will be returned.
|
---|
876 | *
|
---|
877 | * @param $sql SQL string to which the $filter criteria should be added
|
---|
878 | * @param $filter array of filter criteria as pairs of item and pattern
|
---|
879 | * @return SQL string with attached $filter criteria on success
|
---|
880 | * @return the original SQL string on error.
|
---|
881 | *
|
---|
882 | * @author Matthias Grimm <[email protected]>
|
---|
883 | */
|
---|
884 | function _createSQLFilter($sql, $filter) {
|
---|
885 | $SQLfilter = "";
|
---|
886 | $cnt = 0;
|
---|
887 |
|
---|
888 | if ($this->dbcon) {
|
---|
889 | foreach ($filter as $item => $pattern) {
|
---|
890 | $tmp = '%'.$this->_escape($pattern).'%';
|
---|
891 | if ($item == 'user') {
|
---|
892 | if ($cnt++ > 0) $SQLfilter .= " AND ";
|
---|
893 | $SQLfilter .= str_replace('%{user}',$tmp,$this->cnf['FilterLogin']);
|
---|
894 | } else if ($item == 'name') {
|
---|
895 | if ($cnt++ > 0) $SQLfilter .= " AND ";
|
---|
896 | $SQLfilter .= str_replace('%{name}',$tmp,$this->cnf['FilterName']);
|
---|
897 | } else if ($item == 'mail') {
|
---|
898 | if ($cnt++ > 0) $SQLfilter .= " AND ";
|
---|
899 | $SQLfilter .= str_replace('%{email}',$tmp,$this->cnf['FilterEmail']);
|
---|
900 | } else if ($item == 'grps') {
|
---|
901 | if ($cnt++ > 0) $SQLfilter .= " AND ";
|
---|
902 | $SQLfilter .= str_replace('%{group}',$tmp,$this->cnf['FilterGroup']);
|
---|
903 | }
|
---|
904 | }
|
---|
905 |
|
---|
906 | // we have to check SQLfilter here and must not use $cnt because if
|
---|
907 | // any of cnf['Filter????'] is not defined, a malformed SQL string
|
---|
908 | // would be generated.
|
---|
909 |
|
---|
910 | if (strlen($SQLfilter)) {
|
---|
911 | $glue = strpos(strtolower($sql),"where") ? " AND " : " WHERE ";
|
---|
912 | $sql = $sql.$glue.$SQLfilter;
|
---|
913 | }
|
---|
914 | }
|
---|
915 |
|
---|
916 | return $sql;
|
---|
917 | }
|
---|
918 |
|
---|
919 | /**
|
---|
920 | * Escape a string for insertion into the database
|
---|
921 | *
|
---|
922 | * @author Andreas Gohr <[email protected]>
|
---|
923 | * @param string $string The string to escape
|
---|
924 | * @param boolean $like Escape wildcard chars as well?
|
---|
925 | */
|
---|
926 | function _escape($string,$like=false){
|
---|
927 | if($this->dbcon){
|
---|
928 | $string = mysql_real_escape_string($string, $this->dbcon);
|
---|
929 | }else{
|
---|
930 | $string = addslashes($string);
|
---|
931 | }
|
---|
932 | if($like){
|
---|
933 | $string = addcslashes($string,'%_');
|
---|
934 | }
|
---|
935 | return $string;
|
---|
936 | }
|
---|
937 | }
|
---|
938 |
|
---|
939 | //Setup VIM: ex: et ts=2 :
|
---|