source: branches/feature-module-update/html/test/kakinaka/pear/Auth/Container/DB.php @ 15079

Revision 15079, 17.6 KB checked in by nanasess, 17 years ago (diff)

svn:mime-type application/x-httpd-php; charset=UTF-8 設定

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