ここの情報は古いです。ご理解頂いた上でお取り扱いください。

source: OpenPNE/trunk/lib/include/Auth/Container/MDB.php @ 4883

Last change on this file since 4883 was 4883, checked in by ebihara, 14 years ago

#1749:PEAR::Authを1.3.0から1.5.4にバージョンアップ

File size: 19.5 KB
Line 
1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
3
4/**
5 * Storage driver for use against PEAR MDB
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     Lorenzo Alberton <l.alberton@quipo.it>
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: MDB.php,v 1.35 2007/06/12 03:11:26 aashley Exp $
22 * @link       http://pear.php.net/package/Auth
23 * @since      File available since Release 1.2.3
24 */
25
26/**
27 * Include Auth_Container base class
28 */
29require_once 'Auth/Container.php';
30/**
31 * Include PEAR MDB package
32 */
33require_once 'MDB.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 MDB abstraction layer to fetch login data.
40 *
41 * @category   Authentication
42 * @package    Auth
43 * @author     Lorenzo Alberton <l.alberton@quipo.it>
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.5.4  File: $Revision: 1.35 $
48 * @link       http://pear.php.net/package/Auth
49 * @since      Class available since Release 1.2.3
50 */
51class Auth_Container_MDB extends Auth_Container
52{
53
54    // {{{ properties
55
56    /**
57     * Additional options for the storage container
58     * @var array
59     */
60    var $options = array();
61
62    /**
63     * MDB object
64     * @var object
65     */
66    var $db = null;
67    var $dsn = '';
68
69    /**
70     * User that is currently selected from the DB.
71     * @var string
72     */
73    var $activeUser = '';
74
75    // }}}
76    // {{{ Auth_Container_MDB() [constructor]
77
78    /**
79     * Constructor of the container class
80     *
81     * Initate connection to the database via PEAR::MDB
82     *
83     * @param  string Connection data or MDB object
84     * @return object Returns an error object if something went wrong
85     */
86    function Auth_Container_MDB($dsn)
87    {
88        $this->_setDefaults();
89
90        if (is_array($dsn)) {
91            $this->_parseOptions($dsn);
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  mixed DSN string | array | mdb object
108     * @return mixed  Object on error, otherwise bool
109     */
110    function _connect($dsn)
111    {
112        $this->log('Auth_Container_MDB::_connect() called.', AUTH_LOG_DEBUG);
113        if (is_string($dsn) || is_array($dsn)) {
114            $this->db =& MDB::connect($dsn, $this->options['db_options']);
115        } elseif (is_subclass_of($dsn, 'mdb_common')) {
116            $this->db = $dsn;
117        } elseif (is_object($dsn) && MDB::isError($dsn)) {
118            return PEAR::raiseError($dsn->getMessage(), $dsn->code);
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
129        if (MDB::isError($this->db) || PEAR::isError($this->db)) {
130            return PEAR::raiseError($this->db->getMessage(), $this->db->code);
131        }
132
133        if ($this->options['auto_quote']) {
134            $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']);
135            $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']);
136            $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']);
137        } else {
138            $this->options['final_table'] = $this->options['table'];
139            $this->options['final_usernamecol'] = $this->options['usernamecol'];
140            $this->options['final_passwordcol'] = $this->options['passwordcol'];
141        }
142
143        return true;
144    }
145
146    // }}}
147    // {{{ _prepare()
148
149    /**
150     * Prepare database connection
151     *
152     * This function checks if we have already opened a connection to
153     * the database. If that's not the case, a new connection is opened.
154     *
155     * @access private
156     * @return mixed True or a MDB error object.
157     */
158    function _prepare()
159    {
160        if (is_subclass_of($this->db, 'mdb_common')) {
161            return true;
162        }
163        return $this->_connect($this->options['dsn']);
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 MDB_result object or MDB_OK on success, a MDB
179     *                or PEAR error on failure
180     */
181    function query($query)
182    {
183        $this->log('Auth_Container_MDB::query() called.', AUTH_LOG_DEBUG);
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['db_where']    = '';
210        $this->options['auto_quote']  = true;
211    }
212
213    // }}}
214    // {{{ _parseOptions()
215
216    /**
217     * Parse options passed to the container class
218     *
219     * @access private
220     * @param  array
221     */
222    function _parseOptions($array)
223    {
224        foreach ($array as $key => $value) {
225            if (isset($this->options[$key])) {
226                $this->options[$key] = $value;
227            }
228        }
229    }
230
231    // }}}
232    // {{{ _quoteDBFields()
233
234    /**
235     * Quote the db_fields option to avoid the possibility of SQL injection.
236     *
237     * @access private
238     * @return string A properly quoted string that can be concatenated into a
239     * SELECT clause.
240     */
241    function _quoteDBFields()
242    {
243        if (isset($this->options['db_fields'])) {
244            if (is_array($this->options['db_fields'])) {
245                if ($this->options['auto_quote']) {
246                    $fields = array();
247                    foreach ($this->options['db_fields'] as $field) {
248                        $fields[] = $this->db->quoteIdentifier($field);
249                    }
250                    return implode(', ', $fields);
251                } else {
252                    return implode(', ', $this->options['db_fields']);
253                }
254            } else {
255                if (strlen($this->options['db_fields']) > 0) {
256                    if ($this->options['auto_quote']) {
257                        return $this->db->quoteIdentifier($this->options['db_fields']);
258                    } else {
259                        return $this->options['db_fields'];
260                    }
261                }
262            }
263        }
264
265        return '';
266    }
267
268    // }}}
269    // {{{ fetchData()
270
271    /**
272     * Get user information from database
273     *
274     * This function uses the given username to fetch
275     * the corresponding login data from the database
276     * table. If an account that matches the passed username
277     * and password is found, the function returns true.
278     * Otherwise it returns false.
279     *
280     * @param   string Username
281     * @param   string Password
282     * @param   boolean If true password is secured using a md5 hash
283     *                  the frontend and auth are responsible for making sure the container supports
284     *                  challenge response password authentication
285     * @return  mixed  Error object or boolean
286     */
287    function fetchData($username, $password, $isChallengeResponse=false)
288    {
289        $this->log('Auth_Container_MDB::fetchData() called.', AUTH_LOG_DEBUG);
290        // Prepare for a database query
291        $err = $this->_prepare();
292        if ($err !== true) {
293            return PEAR::raiseError($err->getMessage(), $err->getCode());
294        }
295
296        //Check if db_fields contains a *, if so assume all columns are selected
297        if (is_string($this->options['db_fields'])
298            && strstr($this->options['db_fields'], '*')) {
299            $sql_from = '*';
300        } else {
301            $sql_from = $this->options['final_usernamecol'].
302                ", ".$this->options['final_passwordcol'];
303
304            if (strlen($fields = $this->_quoteDBFields()) > 0) {
305                $sql_from .= ', '.$fields;
306            }
307        }
308
309        $query = sprintf("SELECT %s FROM %s WHERE %s = %s",
310                         $sql_from,
311                         $this->options['final_table'],
312                         $this->options['final_usernamecol'],
313                         $this->db->getTextValue($username)
314                         );
315
316        // check if there is an optional parameter db_where
317        if ($this->options['db_where'] != '') {
318            // there is one, so add it to the query
319            $query .= " AND ".$this->options['db_where'];
320        }
321
322        $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG);
323
324        $res = $this->db->getRow($query, null, null, null, MDB_FETCHMODE_ASSOC);
325
326        if (MDB::isError($res) || PEAR::isError($res)) {
327            return PEAR::raiseError($res->getMessage(), $res->getCode());
328        }
329        if (!is_array($res)) {
330            $this->activeUser = '';
331            return false;
332        }
333
334        // Perform trimming here before the hashing
335        $password = trim($password, "\r\n");
336        $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n");
337
338        // If using Challenge Response md5 the pass with the secret
339        if ($isChallengeResponse) {
340            $res[$this->options['passwordcol']] =
341                md5($res[$this->options['passwordcol']].$this->_auth_obj->session['loginchallenege']);
342            // UGLY cannot avoid without modifying verifyPassword
343            if ($this->options['cryptType'] == 'md5') {
344                $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]);
345            }
346        }
347
348        if ($this->verifyPassword($password,
349                                  $res[$this->options['passwordcol']],
350                                  $this->options['cryptType'])) {
351            // Store additional field values in the session
352            foreach ($res as $key => $value) {
353                if ($key == $this->options['passwordcol'] ||
354                    $key == $this->options['usernamecol']) {
355                    continue;
356                }
357
358                $this->log('Storing additional field: '.$key, AUTH_LOG_DEBUG);
359                // Use reference to the auth object if exists
360                // This is because the auth session variable can change so a static
361                // call to setAuthData does not make sense
362                $this->_auth_obj->setAuthData($key, $value);
363            }
364            return true;
365        }
366
367        $this->activeUser = $res[$this->options['usernamecol']];
368        return false;
369    }
370
371    // }}}
372    // {{{ listUsers()
373
374    /**
375     * Returns a list of users from the container
376     *
377     * @return mixed array|PEAR_Error
378     * @access public
379     */
380    function listUsers()
381    {
382        $this->log('Auth_Container_MDB::listUsers() called.', AUTH_LOG_DEBUG);
383        $err = $this->_prepare();
384        if ($err !== true) {
385            return PEAR::raiseError($err->getMessage(), $err->getCode());
386        }
387
388        $retVal = array();
389
390        //Check if db_fields contains a *, if so assume all columns are selected
391        if (   is_string($this->options['db_fields'])
392            && strstr($this->options['db_fields'], '*')) {
393            $sql_from = '*';
394        } else {
395            $sql_from = $this->options['final_usernamecol']
396                .', '.$this->options['final_passwordcol'];
397
398            if (strlen($fields = $this->_quoteDBFields()) > 0) {
399                $sql_from .= ', '.$fields;
400            }
401        }
402
403        $query = sprintf('SELECT %s FROM %s',
404                         $sql_from,
405                         $this->options['final_table']
406                         );
407
408        // check if there is an optional parameter db_where
409        if ($this->options['db_where'] != '') {
410            // there is one, so add it to the query
411            $query .= " WHERE ".$this->options['db_where'];
412        }
413
414        $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG);
415
416        $res = $this->db->getAll($query, null, null, null, MDB_FETCHMODE_ASSOC);
417
418        if (MDB::isError($res)) {
419            return PEAR::raiseError($res->getMessage(), $res->getCode());
420        } else {
421            foreach ($res as $user) {
422                $user['username'] = $user[$this->options['usernamecol']];
423                $retVal[] = $user;
424            }
425        }
426        $this->log('Found '.count($retVal).' users.', AUTH_LOG_DEBUG);
427        return $retVal;
428    }
429
430    // }}}
431    // {{{ addUser()
432
433    /**
434     * Add user to the storage container
435     *
436     * @access public
437     * @param  string Username
438     * @param  string Password
439     * @param  mixed  Additional information that are stored in the DB
440     *
441     * @return mixed True on success, otherwise error object
442     */
443    function addUser($username, $password, $additional = "")
444    {
445        $this->log('Auth_Container_MDB::addUser() called.', AUTH_LOG_DEBUG);
446        $err = $this->_prepare();
447        if ($err !== true) {
448            return PEAR::raiseError($err->getMessage(), $err->getCode());
449        }
450
451        if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') {
452            $cryptFunction = 'strval';
453        } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) {
454            $cryptFunction = $this->options['cryptType'];
455        } else {
456            $cryptFunction = 'md5';
457        }
458
459        $password = $cryptFunction($password);
460
461        $additional_key   = '';
462        $additional_value = '';
463
464        if (is_array($additional)) {
465            foreach ($additional as $key => $value) {
466                if ($this->options['auto_quote']) {
467                    $additional_key   .= ', ' . $this->db->quoteIdentifier($key);
468                } else {
469                    $additional_key   .= ', ' . $key;
470                }
471                $additional_value .= ', ' . $this->db->getTextValue($value);
472            }
473        }
474
475        $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)",
476                         $this->options['final_table'],
477                         $this->options['final_usernamecol'],
478                         $this->options['final_passwordcol'],
479                         $additional_key,
480                         $this->db->getTextValue($username),
481                         $this->db->getTextValue($password),
482                         $additional_value
483                         );
484
485        $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG);
486
487        $res = $this->query($query);
488
489        if (MDB::isError($res)) {
490            return PEAR::raiseError($res->getMessage(), $res->code);
491        }
492        return true;
493    }
494
495    // }}}
496    // {{{ removeUser()
497
498    /**
499     * Remove user from the storage container
500     *
501     * @access public
502     * @param  string Username
503     *
504     * @return mixed True on success, otherwise error object
505     */
506    function removeUser($username)
507    {
508        $this->log('Auth_Container_MDB::removeUser() called.', AUTH_LOG_DEBUG);
509        $err = $this->_prepare();
510        if ($err !== true) {
511            return PEAR::raiseError($err->getMessage(), $err->getCode());
512        }
513
514        $query = sprintf("DELETE FROM %s WHERE %s = %s",
515                         $this->options['final_table'],
516                         $this->options['final_usernamecol'],
517                         $this->db->getTextValue($username)
518                         );
519
520        // check if there is an optional parameter db_where
521        if ($this->options['db_where'] != '') {
522            // there is one, so add it to the query
523            $query .= " AND ".$this->options['db_where'];
524        }
525
526        $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG);
527
528        $res = $this->query($query);
529
530        if (MDB::isError($res)) {
531            return PEAR::raiseError($res->getMessage(), $res->code);
532        }
533        return true;
534    }
535
536    // }}}
537    // {{{ changePassword()
538
539    /**
540     * Change password for user in the storage container
541     *
542     * @param string Username
543     * @param string The new password (plain text)
544     */
545    function changePassword($username, $password)
546    {
547        $this->log('Auth_Container_MDB::changePassword() called.', AUTH_LOG_DEBUG);
548        $err = $this->_prepare();
549        if ($err !== true) {
550            return PEAR::raiseError($err->getMessage(), $err->getCode());
551        }
552
553        if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') {
554            $cryptFunction = 'strval';
555        } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) {
556            $cryptFunction = $this->options['cryptType'];
557        } else {
558            $cryptFunction = 'md5';
559        }
560
561        $password = $cryptFunction($password);
562
563        $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s",
564                         $this->options['final_table'],
565                         $this->options['final_passwordcol'],
566                         $this->db->getTextValue($password),
567                         $this->options['final_usernamecol'],
568                         $this->db->getTextValue($username)
569                         );
570
571        // check if there is an optional parameter db_where
572        if ($this->options['db_where'] != '') {
573            // there is one, so add it to the query
574            $query .= " AND ".$this->options['db_where'];
575        }
576
577        $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG);
578
579        $res = $this->query($query);
580
581        if (MDB::isError($res)) {
582            return PEAR::raiseError($res->getMessage(), $res->code);
583        }
584        return true;
585    }
586
587    // }}}
588    // {{{ supportsChallengeResponse()
589
590    /**
591     * Determine if this container supports
592     * password authentication with challenge response
593     *
594     * @return bool
595     * @access public
596     */
597    function supportsChallengeResponse()
598    {
599        return in_array($this->options['cryptType'], array('md5', 'none', ''));
600    }
601
602    // }}}
603    // {{{ getCryptType()
604
605    /**
606     * Returns the selected crypt type for this container
607     *
608     * @return string Function used to crypt the password
609     */
610    function getCryptType()
611    {
612        return $this->options['cryptType'];
613    }
614
615    // }}}
616
617}
618?>
Note: See TracBrowser for help on using the repository browser.