1: <?php
2: /**
3: * The SQL driver attempts to change a user's password stored in an SQL
4: * database and implements the Passwd_Driver API.
5: *
6: * Copyright 2000-2012 Horde LLC (http://www.horde.org/)
7: *
8: * See the enclosed file COPYING for license information (GPL). If you
9: * did not receive this file, see http://www.horde.org/licenses/gpl.php.
10: *
11: * @author Mike Cochrane <mike@graftonhall.co.nz>
12: * @author Ilya Krel <mail@krel.org>
13: * @author Tjeerd van der Zee <admin@xar.nl>
14: * @author Mattias Webjörn Eriksson <mattias@webjorn.org>
15: * @author Eric Jon Rostetter <eric.rostetter@physics.utexas.edu>
16: * @author Ralf Lang <lang@b1-systems.de> (H4 conversion)
17: * @package Passwd
18: */
19: class Passwd_Driver_Sql extends Passwd_Driver
20: {
21: /**
22: * Handle for the current database connection.
23: *
24: * @var Horde_Db_Adapter
25: */
26: protected $_db;
27:
28: /**
29: * Constructor.
30: *
31: * @param array $params Additional parameters needed:
32: * <pre>
33: * 'db' - (Horde_Db_Adapter) A DB Adapter object.
34: * optional:
35: * 'table' - (string) The name of the user database table
36: * 'encryption' - (string) The encryption type
37: * 'user_col' - (string) The table column for user name
38: * 'pass_col' - (string) The table column for password
39: * 'show_encryption' - (boolean) Prepend the encryption type to the password?
40: * 'query_lookup' - (string) Should we use a custom query for lookup?
41: * 'query_modify' - (string) Should we use a custom query for changing?
42: * </pre>
43: *
44: * @throws InvalidArgumentException
45: */
46: public function __construct($params = array())
47: {
48: if (isset($params['db'])) {
49: $this->_db = $params['db'];
50: unset($params['db']);
51: } else {
52: throw new InvalidArgumentException('Missing required Horde_Db_Adapter object');
53: }
54: /* These default to matching the Auth_sql defaults. */
55: $this->_params = array_merge(
56: array('table' => 'horde_users',
57: 'encryption' => 'ssha',
58: 'user_col' => 'user_uid',
59: 'pass_col' => 'user_pass',
60: 'show_encryption' => false,
61: 'query_lookup' => false,
62: 'query_modify' => false),
63: $params);
64: }
65:
66: /**
67: * Finds out if a username and password is valid.
68: *
69: * @param string $userID The userID to check.
70: * @param string $old_password An old password to check.
71: *
72: * @throws Passwd_Exception
73: */
74: protected function _lookup($user, $old_password)
75: {
76: if (!empty($this->_params['query_lookup'])) {
77: list($sql, $values) = $this->_parseQuery($this->_params['query_lookup'], $user, $old_password);
78: } else {
79: /* Build the SQL query. */
80: $sql = 'SELECT ' . $this->_params['pass_col'] . ' FROM ' . $this->_params['table'] .
81: ' WHERE ' . $this->_params['user_col'] . ' = ?';
82: $values = array($user);
83: }
84:
85: /* Run query. */
86: try {
87: $result = $this->_db->selectOne($sql, $values);
88: } catch (Horde_Db_Exception $e) {
89: throw new Passwd_Exception($e);
90: }
91:
92: if (is_array($result)) {
93: $current_password = $result[$this->_params['pass_col']];
94: } else {
95: throw new Passwd_Exception(_("User not found"));
96: }
97:
98: /* Check the passwords match. */
99: $this->_comparePasswords($current_password, $old_password);
100: }
101:
102: /**
103: * Modifies a SQL password record for a user.
104: *
105: * @param string $user The user whose record we will udpate.
106: * @param string $new_password The new password value to set.
107: *
108: * @throws Passwd_Exception
109: */
110: protected function _modify($user, $new_password)
111: {
112: if (!empty($this->_params['query_modify'])) {
113: list($sql, $values) = $this->_parseQuery($this->_params['query_modify'], $user, $new_password);
114: } else {
115: /* Encrypt the password. */
116: $new_password = $this->_encryptPassword($new_password);
117:
118: /* Build the SQL query. */
119: $sql = 'UPDATE ' . $this->_params['table'] .
120: ' SET ' . $this->_params['pass_col'] . ' = ?' .
121: ' WHERE ' . $this->_params['user_col'] . ' = ?';
122: $values = array($new_password, $user);
123: }
124:
125: /* Execute the query. */
126: try {
127: $this->_db->update($sql, $values);
128: } catch (Horde_Db_Exception $e) {
129: throw new Passwd_Exception($e);
130: }
131: }
132:
133: /**
134: * Parses the string as an SQL query substituting placeholders for
135: * their values.
136: *
137: * @param string $string The string to process as a query.
138: * @param string $user The user to use for the %u placeholder.
139: * @param string $password The password to use for the %p and %e placeholders.
140: *
141: * @return string The processed SQL query.
142: */
143: protected function _parseQuery($string, $user, $password)
144: {
145: $query = '';
146: $values = array();
147: $length = strlen($string);
148: @list($username, $domain) = explode('@', $user);
149: for ($i = 0; $i < $length; $i++) {
150: if ($string[$i] == '%' && !empty($string[$i + 1])) {
151: switch ($string[++$i]) {
152: case 'd':
153: $query .= '?';
154: $values[] = $domain;
155: break;
156:
157: case 'u':
158: $query .= '?';
159: $values[] = $user;
160: break;
161:
162: case 'U':
163: $query .= '?';
164: $values[] = $username;
165: break;
166:
167: case 'p':
168: $query .= '?';
169: $values[] = $password;
170: break;
171:
172: case 'e':
173: $query .= '?';
174: $values[] = $this->_encryptPassword($password);
175: break;
176:
177: case '%':
178: $query .= '%';
179: break;
180:
181: default:
182: $query .= '%' . $string[$i];
183: break;
184: }
185: } else {
186: $query .= $string[$i];
187: }
188: }
189:
190: return array($query, $values);
191: }
192:
193: /**
194: * Changes the user's password.
195: *
196: * @param string $username The user for which to change the password.
197: * @param string $old_password The old (current) user password.
198: * @param string $new_password The new user password to set.
199: *
200: * @throws Passwd_Exception
201: */
202: public function changePassword($username, $old_password, $new_password)
203: {
204: /* Check the current password. */
205: $this->_lookup($username, $old_password);
206: $this->_modify($username, $new_password);
207: }
208: }
209: