source: temp/trunk/html/test/kakinaka/pear/Auth/Container/DB.php @ 10038

Revision 10038, 17.5 KB checked in by kakinaka, 20 years ago (diff)

* empty log message *

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
3
4/**
5 * Storage driver for use against PEAR DB
6 *
7 * PHP versions 4 and 5
8 *
9 * LICENSE: This source file is subject to version 3.01 of the PHP license
10 * that is available through the world-wide-web at the following URI:
11 * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
12 * the PHP License and are unable to obtain it through the web, please
13 * send a note to [email protected] so we can mail you a copy immediately.
14 *
15 * @category   Authentication
16 * @package    Auth
17 * @author     Martin Jansen <[email protected]>
18 * @author     Adam Ashley <[email protected]>
19 * @copyright  2001-2006 The PHP Group
20 * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
21 * @version    CVS: $Id$
22 * @link       http://pear.php.net/package/Auth
23 */
24
25/**
26 * Include Auth_Container base class
27 */
28require_once 'Auth/Container.php';
29/**
30 * Include PEAR DB
31 */
32require_once 'DB.php';
33
34/**
35 * Storage driver for fetching login data from a database
36 *
37 * This storage driver can use all databases which are supported
38 * by the PEAR DB abstraction layer to fetch login data.
39 *
40 * @category   Authentication
41 * @package    Auth
42 * @author     Martin Jansen <[email protected]>
43 * @author     Adam Ashley <[email protected]>
44 * @copyright  2001-2006 The PHP Group
45 * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
46 * @version    Release: 1.4.2  File: $Revision$
47 * @link       http://pear.php.net/package/Auth
48 */
49class Auth_Container_DB extends Auth_Container
50{
51
52    // {{{ properties
53
54    /**
55     * Additional options for the storage container
56     * @var array
57     */
58    var $options = array();
59
60    /**
61     * DB object
62     * @var object
63     */
64    var $db = null;
65    var $dsn = '';
66
67    /**
68     * User that is currently selected from the DB.
69     * @var string
70     */
71    var $activeUser = '';
72
73    // }}}
74    // {{{ Auth_Container_DB [constructor]
75
76    /**
77     * Constructor of the container class
78     *
79     * Save the initial options passed to the container. Initiation of the DB
80     * connection is no longer performed here and is only done when needed.
81     *
82     * @param  string Connection data or DB object
83     * @return object Returns an error object if something went wrong
84     */
85    function Auth_Container_DB($dsn)
86    {
87        $this->_setDefaults();
88
89        if (is_array($dsn)) {
90            $this->_parseOptions($dsn);
91
92            if (empty($this->options['dsn'])) {
93                PEAR::raiseError('No connection parameters specified!');
94            }
95        } else {
96            $this->options['dsn'] = $dsn;
97        }
98    }
99
100    // }}}
101    // {{{ _connect()
102
103    /**
104     * Connect to database by using the given DSN string
105     *
106     * @access private
107     * @param  string DSN string
108     * @return mixed  Object on error, otherwise bool
109     */
110    function _connect($dsn)
111    {
112        if (is_string($dsn) || is_array($dsn)) {
113            $this->db = DB::Connect($dsn, $this->options['db_options']);
114        } elseif (is_subclass_of($dsn, 'db_common')) {
115            $this->db = $dsn;
116        } elseif (DB::isError($dsn)) {
117            return PEAR::raiseError($dsn->getMessage(), $dsn->getCode());
118        } else {
119            return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__,
120                                    41,
121                                    PEAR_ERROR_RETURN,
122                                    null,
123                                    null
124                                    );
125        }
126
127        if (DB::isError($this->db) || PEAR::isError($this->db)) {
128            return PEAR::raiseError($this->db->getMessage(), $this->db->getCode());
129        } else {
130            return true;
131        }
132    }
133
134    // }}}
135    // {{{ _prepare()
136
137    /**
138     * Prepare database connection
139     *
140     * This function checks if we have already opened a connection to
141     * the database. If that's not the case, a new connection is opened.
142     *
143     * @access private
144     * @return mixed True or a DB error object.
145     */
146    function _prepare()
147    {
148        if (!DB::isConnection($this->db)) {
149            $res = $this->_connect($this->options['dsn']);
150            if (DB::isError($res) || PEAR::isError($res)) {
151                return $res;
152            }
153        }
154        if ($this->options['auto_quote'] && $this->db->dsn['phptype'] != 'sqlite') {
155            $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']);
156            $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']);
157            $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']);
158        } else {
159            $this->options['final_table'] = $this->options['table'];
160            $this->options['final_usernamecol'] = $this->options['usernamecol'];
161            $this->options['final_passwordcol'] = $this->options['passwordcol'];
162        }
163        return true;
164    }
165
166    // }}}
167    // {{{ query()
168
169    /**
170     * Prepare query to the database
171     *
172     * This function checks if we have already opened a connection to
173     * the database. If that's not the case, a new connection is opened.
174     * After that the query is passed to the database.
175     *
176     * @access public
177     * @param  string Query string
178     * @return mixed  a DB_result object or DB_OK on success, a DB
179     *                or PEAR error on failure
180     */
181    function query($query)
182    {
183        $err = $this->_prepare();
184        if ($err !== true) {
185            return $err;
186        }
187        return $this->db->query($query);
188    }
189
190    // }}}
191    // {{{ _setDefaults()
192
193    /**
194     * Set some default options
195     *
196     * @access private
197     * @return void
198     */
199    function _setDefaults()
200    {
201        $this->options['table']       = 'auth';
202        $this->options['usernamecol'] = 'username';
203        $this->options['passwordcol'] = 'password';
204        $this->options['dsn']         = '';
205        $this->options['db_fields']   = '';
206        $this->options['cryptType']   = 'md5';
207        $this->options['db_options']  = array();
208        $this->options['auto_quote']  = true;
209    }
210
211    // }}}
212    // {{{ _parseOptions()
213
214    /**
215     * Parse options passed to the container class
216     *
217     * @access private
218     * @param  array
219     */
220    function _parseOptions($array)
221    {
222        foreach ($array as $key => $value) {
223            if (isset($this->options[$key])) {
224                $this->options[$key] = $value;
225            }
226        }
227    }
228
229    // }}}
230    // {{{ _quoteDBFields()
231
232    /**
233     * Quote the db_fields option to avoid the possibility of SQL injection.
234     *
235     * @access private
236     * @return string A properly quoted string that can be concatenated into a
237     * SELECT clause.
238     */
239    function _quoteDBFields()
240    {
241        if (isset($this->options['db_fields'])) {
242            if (is_array($this->options['db_fields'])) {
243                if ($this->options['auto_quote']) {
244                    $fields = array();
245                    foreach ($this->options['db_fields'] as $field) {
246                        $fields[] = $this->db->quoteIdentifier($field);
247                    }
248                    return implode(', ', $fields);
249                } else {
250                    return implode(', ', $this->options['db_fields']);
251                }
252            } else {
253                if (strlen($this->options['db_fields']) > 0) {
254                    if ($this->options['auto_quote']) {
255                        return $this->db->quoteIdentifier($this->options['db_fields']);
256                    } else {
257                        return $this->options['db_fields'];
258                    }
259                }
260            }
261        }
262
263        return '';
264    }
265   
266    // }}}
267    // {{{ fetchData()
268
269    /**
270     * Get user information from database
271     *
272     * This function uses the given username to fetch
273     * the corresponding login data from the database
274     * table. If an account that matches the passed username
275     * and password is found, the function returns true.
276     * Otherwise it returns false.
277     *
278     * @param   string Username
279     * @param   string Password
280     * @param   boolean If true password is secured using a md5 hash
281     *                  the frontend and auth are responsible for making sure the container supports
282     *                  challenge response password authentication
283     * @return  mixed  Error object or boolean
284     */
285    function fetchData($username, $password, $isChallengeResponse=false)
286    {
287        // Prepare for a database query
288        $err = $this->_prepare();
289        if ($err !== true) {
290            return PEAR::raiseError($err->getMessage(), $err->getCode());
291        }
292
293        // Find if db_fields contains a *, if so assume all columns are selected
294        if (is_string($this->options['db_fields'])
295            && strstr($this->options['db_fields'], '*')) {
296            $sql_from = "*";
297        } else {
298            $sql_from = $this->options['final_usernamecol'].
299                ", ".$this->options['final_passwordcol'];
300
301            if (strlen($fields = $this->_quoteDBFields()) > 0) {
302                $sql_from .= ', '.$fields;
303            }
304        }
305
306        $query = "SELECT ".$sql_from.
307                " FROM ".$this->options['final_table'].
308                " WHERE ".$this->options['final_usernamecol']." = ".$this->db->quoteSmart($username);
309
310        $res = $this->db->getRow($query, null, DB_FETCHMODE_ASSOC);
311
312        if (DB::isError($res)) {
313            return PEAR::raiseError($res->getMessage(), $res->getCode());
314        }
315
316        if (!is_array($res)) {
317            $this->activeUser = '';
318            return false;
319        }
320
321        // Perform trimming here before the hashihg
322        $password = trim($password, "\r\n");
323        $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n");
324
325        // If using Challenge Response md5 the pass with the secret
326        if ($isChallengeResponse) {
327            $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]
328                    .$this->_auth_obj->session['loginchallenege']);
329           
330            // UGLY cannot avoid without modifying verifyPassword
331            if ($this->options['cryptType'] == 'md5') {
332                $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]);
333            }
334           
335            //print " Hashed Password [{$res[$this->options['passwordcol']]}]<br/>\n";
336        }
337
338        if ($this->verifyPassword($password,
339                                  $res[$this->options['passwordcol']],
340                                  $this->options['cryptType'])) {
341            // Store additional field values in the session
342            foreach ($res as $key => $value) {
343                if ($key == $this->options['passwordcol'] ||
344                    $key == $this->options['usernamecol']) {
345                    continue;
346                }
347                // Use reference to the auth object if exists
348                // This is because the auth session variable can change so a
349                // static call to setAuthData does not make sence
350                $this->_auth_obj->setAuthData($key, $value);
351            }
352            return true;
353        }
354        $this->activeUser = $res[$this->options['usernamecol']];
355        return false;
356    }
357
358    // }}}
359    // {{{ listUsers()
360
361    /**
362     * Returns a list of users from the container
363     *
364     * @return mixed
365     * @access public
366     */
367    function listUsers()
368    {
369        $err = $this->_prepare();
370        if ($err !== true) {
371            return PEAR::raiseError($err->getMessage(), $err->getCode());
372        }
373
374        $retVal = array();
375
376        // Find if db_fields contains a *, if so assume all col are selected
377        if (   is_string($this->options['db_fields'])
378            && strstr($this->options['db_fields'], '*')) {
379            $sql_from = "*";
380        } else {
381            $sql_from = $this->options['final_usernamecol'].
382                ", ".$this->options['final_passwordcol'];
383
384            if (strlen($fields = $this->_quoteDBFields()) > 0) {
385                $sql_from .= ', '.$fields;
386            }
387        }
388
389        $query = sprintf("SELECT %s FROM %s",
390                         $sql_from,
391                         $this->options['final_table']
392                         );
393        $res = $this->db->getAll($query, null, DB_FETCHMODE_ASSOC);
394
395        if (DB::isError($res)) {
396            return PEAR::raiseError($res->getMessage(), $res->getCode());
397        } else {
398            foreach ($res as $user) {
399                $user['username'] = $user[$this->options['usernamecol']];
400                $retVal[] = $user;
401            }
402        }
403        return $retVal;
404    }
405
406    // }}}
407    // {{{ addUser()
408
409    /**
410     * Add user to the storage container
411     *
412     * @access public
413     * @param  string Username
414     * @param  string Password
415     * @param  mixed  Additional information that are stored in the DB
416     *
417     * @return mixed True on success, otherwise error object
418     */
419    function addUser($username, $password, $additional = "")
420    {
421        $err = $this->_prepare();
422        if ($err !== true) {
423            return PEAR::raiseError($err->getMessage(), $err->getCode());
424        }
425
426        if (   isset($this->options['cryptType'])
427            && $this->options['cryptType'] == 'none') {
428            $cryptFunction = 'strval';
429        } elseif (   isset($this->options['cryptType'])
430                  && function_exists($this->options['cryptType'])) {
431            $cryptFunction = $this->options['cryptType'];
432        } else {
433            $cryptFunction = 'md5';
434        }
435
436        $password = $cryptFunction($password);
437
438        $additional_key   = '';
439        $additional_value = '';
440
441        if (is_array($additional)) {
442            foreach ($additional as $key => $value) {
443                if ($this->options['auto_quote']) {
444                    $additional_key .= ', ' . $this->db->quoteIdentifier($key);
445                } else {
446                    $additional_key .= ', ' . $key;
447                }
448                $additional_value .= ", " . $this->db->quoteSmart($value);
449            }
450        }
451
452        $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)",
453                         $this->options['final_table'],
454                         $this->options['final_usernamecol'],
455                         $this->options['final_passwordcol'],
456                         $additional_key,
457                         $this->db->quoteSmart($username),
458                         $this->db->quoteSmart($password),
459                         $additional_value
460                         );
461
462        $res = $this->query($query);
463
464        if (DB::isError($res)) {
465            return PEAR::raiseError($res->getMessage(), $res->getCode());
466        } else {
467            return true;
468        }
469    }
470
471    // }}}
472    // {{{ removeUser()
473
474    /**
475     * Remove user from the storage container
476     *
477     * @access public
478     * @param  string Username
479     *
480     * @return mixed True on success, otherwise error object
481     */
482    function removeUser($username)
483    {
484        $err = $this->_prepare();
485        if ($err !== true) {
486            return PEAR::raiseError($err->getMessage(), $err->getCode());
487        }
488
489        $query = sprintf("DELETE FROM %s WHERE %s = %s",
490                         $this->options['final_table'],
491                         $this->options['final_usernamecol'],
492                         $this->db->quoteSmart($username)
493                         );
494
495        $res = $this->query($query);
496
497        if (DB::isError($res)) {
498           return PEAR::raiseError($res->getMessage(), $res->getCode());
499        } else {
500          return true;
501        }
502    }
503
504    // }}}
505    // {{{ changePassword()
506
507    /**
508     * Change password for user in the storage container
509     *
510     * @param string Username
511     * @param string The new password (plain text)
512     */
513    function changePassword($username, $password)
514    {
515        $err = $this->_prepare();
516        if ($err !== true) {
517            return PEAR::raiseError($err->getMessage(), $err->getCode());
518        }
519
520        if (   isset($this->options['cryptType'])
521            && $this->options['cryptType'] == 'none') {
522            $cryptFunction = 'strval';
523        } elseif (   isset($this->options['cryptType'])
524                  && function_exists($this->options['cryptType'])) {
525            $cryptFunction = $this->options['cryptType'];
526        } else {
527            $cryptFunction = 'md5';
528        }
529
530        $password = $cryptFunction($password);
531
532        $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s",
533                         $this->options['final_table'],
534                         $this->options['final_passwordcol'],
535                         $this->db->quoteSmart($password),
536                         $this->options['final_usernamecol'],
537                         $this->db->quoteSmart($username)
538                         );
539
540        $res = $this->query($query);
541
542        if (DB::isError($res)) {
543            return PEAR::raiseError($res->getMessage(), $res->getCode());
544        } else {
545            return true;
546        }
547    }
548
549    // }}}
550    // {{{ supportsChallengeResponse()
551
552    /**
553     * Determine if this container supports
554     * password authentication with challenge response
555     *
556     * @return bool
557     * @access public
558     */
559    function supportsChallengeResponse()
560    {
561        return in_array($this->options['cryptType'], array('md5', 'none', ''));
562    }
563
564    // }}}
565    // {{{ getCryptType()
566
567    /**
568      * Returns the selected crypt type for this container
569      */
570    function getCryptType()
571    {
572        return($this->options['cryptType']);
573    }
574
575    // }}}
576
577}
578?>
Note: See TracBrowser for help on using the repository browser.