1: <?php
2: /**
3: * The Horde_Auth_Customsql class provides a sql implementation of the Horde
4: * authentication system with the possibility to set custom-made queries.
5: *
6: * Copyright 2002 Ronnie Garcia <ronnie@mk2.net>
7: * Copyright 2002-2012 Horde LLC (http://www.horde.org/)
8: *
9: * See the enclosed file COPYING for license information (LGPL). If you did
10: * not receive this file, http://www.horde.org/licenses/lgpl21
11: *
12: * @author Ronnie Garcia <ronnie@mk2.net>
13: * @author Chuck Hagenbuch <chuck@horde.org>
14: * @author Joel Vandal <joel@scopserv.com>
15: * @category Horde
16: * @license http://opensource.org/licenses/lgpl-2.1.php LGPL-2.1
17: * @package Auth
18: */
19: class Horde_Auth_Customsql extends Horde_Auth_Sql
20: {
21: /**
22: * An array of capabilities, so that the driver can report which
23: * operations it supports and which it doesn't.
24: *
25: * @var array
26: */
27: protected $_capabilities = array(
28: 'add' => true,
29: 'list' => true,
30: 'remove' => true,
31: 'resetpassword' => true,
32: 'update' => true,
33: 'authenticate' => true,
34: );
35:
36: /**
37: * Constructor.
38: *
39: * Some special tokens can be used in the SQL query. They are replaced
40: * at the query stage:
41: * '\L' will be replaced by the user's login
42: * '\P' will be replaced by the user's password.
43: * '\O' will be replaced by the old user's login (required for update)
44: *
45: * Eg: "SELECT * FROM users WHERE uid = \L
46: * AND passwd = \P
47: * AND billing = 'paid'"
48: *
49: * @param array $params Configuration parameters:
50: * - query_auth: (string) Authenticate the user. ('\L' & '\P')
51: * - query_add: (string) Add user. ('\L' & '\P')
52: * - query_getpw: (string) Get one user's password. ('\L')
53: * - query_update: (string) Update user. ('\O', '\L' & '\P')
54: * - query_resetpassword: (string) Reset password. ('\L', & '\P')
55: * - query_remove: (string) Remove user. ('\L')
56: * - query_list: (string) List user.
57: * - query_exists: (string) Check for existance of user. ('\L')
58: */
59: public function __construct(array $params = array())
60: {
61: foreach (array('query_auth', 'query_add',
62: 'query_update', 'query_resetpassword', 'query_remove',
63: 'query_list') as $val) {
64: if (empty($params[$val])) {
65: switch($val) {
66: case 'query_auth':
67: $this->_capabilities['authenticate'] = false;
68: break;
69: case 'query_add':
70: $this->_capabilities['add'] = false;
71: break;
72: case 'query_update':
73: $this->_capabilities['update'] = false;
74: break;
75: case 'query_resetpassword':
76: $this->_capabilities['resetpassword'] = false;
77: break;
78: case 'query_remove':
79: $this->_capabilities['remove'] = false;
80: break;
81: case 'query_list':
82: $this->_capabilities['list'] = false;
83: break;
84: }
85: }
86: }
87:
88: parent::__construct($params);
89: }
90:
91: /**
92: * Find out if a set of login credentials are valid.
93: *
94: * @param string $userId The userId to check.
95: * @param array $credentials The credentials to use.
96: *
97: * @throws Horde_Auth_Exception
98: */
99: protected function _authenticate($userId, $credentials)
100: {
101: /* Build a custom query, based on the config file. */
102: $query = str_replace(
103: array('\L', '\P'),
104: array(
105: $this->_db->quote($userId),
106: $this->_db->quote(Horde_Auth::getCryptedPassword($credentials['password'], $this->_getPassword($userId), $this->_params['encryption'], $this->_params['show_encryption']))
107: ),
108: $this->_params['query_auth']
109: );
110:
111: try {
112: if ($this->_db->selectValue($query)) {
113: return;
114: }
115: throw new Horde_Auth_Exception('', Horde_Auth::REASON_BADLOGIN);
116: } catch (Horde_Db_Exception $e) {
117: throw new Horde_Auth_Exception('', Horde_Auth::REASON_FAILED);
118: }
119: }
120:
121: /**
122: * Add a set of authentication credentials.
123: *
124: * @param string $userId The userId to add.
125: * @param array $credentials The credentials to add.
126: *
127: * @throws Horde_Auth_Exception
128: */
129: public function addUser($userId, $credentials)
130: {
131: /* Build a custom query, based on the config file. */
132: $query = str_replace(
133: array('\L', '\P'),
134: array(
135: $this->_db->quote($userId),
136: $this->_db->quote(Horde_Auth::getCryptedPassword($credentials['password'], '', $this->_params['encryption'], $this->_params['show_encryption']))
137: ),
138: $this->_params['query_add']
139: );
140:
141: try {
142: $this->_db->insert($query);
143: } catch (Horde_Db_Exception $e) {
144: throw new Horde_Auth_Exception($e);
145: }
146: }
147:
148: /**
149: * Update a set of authentication credentials.
150: *
151: * @param string $oldId The old userId.
152: * @param string $newId The new userId.
153: * @param array $credentials The new credentials
154: *
155: * @throws Horde_Auth_Exception
156: */
157: public function updateUser($oldId, $newId, $credentials)
158: {
159: /* Build a custom query, based on the config file. */
160: $query = str_replace(
161: array('\O', '\L', '\P'),
162: array(
163: $this->_db->quote($oldId),
164: $this->_db->quote($newId),
165: $this->_db->quote(Horde_Auth::getCryptedPassword($credentials['password'], $this->_getPassword($oldId), $this->_params['encryption'], $this->_params['show_encryption']))
166: ),
167: $this->_params['query_update']
168: );
169:
170: try {
171: $this->_db->update($query);
172: } catch (Horde_Db_Exception $e) {
173: throw new Horde_Auth_Exception($e);
174: }
175: }
176:
177: /**
178: * Resets a user's password. Used for example when the user does not
179: * remember the existing password.
180: *
181: * @param string $userId The user id for which to reset the password.
182: *
183: * @return string The new password on success.
184: * @throws Horde_Auth_Exception
185: */
186: public function resetPassword($userId)
187: {
188: /* Get a new random password. */
189: $password = Horde_Auth::genRandomPassword();
190:
191: /* Build the SQL query. */
192: $query = str_replace(
193: array('\L', '\P'),
194: array(
195: $this->_db->quote($userId),
196: $this->_db->quote(Horde_Auth::getCryptedPassword($password, '', $this->_params['encryption'], $this->_params['show_encryption']))
197: ),
198: $this->_params['query_resetpassword']
199: );
200:
201: try {
202: $this->_db->update($query);
203: } catch (Horde_Db_Exception $e) {
204: throw new Horde_Auth_Exception($e);
205: }
206:
207: return $password;
208: }
209:
210: /**
211: * Delete a set of authentication credentials.
212: *
213: * @param string $userId The userId to delete.
214: *
215: * @throws Horde_Auth_Exception
216: */
217: public function removeUser($userId)
218: {
219: /* Build a custom query, based on the config file. */
220: $query = str_replace(
221: '\L',
222: $this->_db->quote($userId),
223: $this->_params['query_remove']
224: );
225:
226: try {
227: $this->_db->delete($query);
228: } catch (Horde_Db_Exception $e) {
229: throw new Horde_Auth_Exception($e);
230: }
231: }
232:
233: /**
234: * List all users in the system.
235: *
236: * @return array The array of userIds.
237: * @throws Horde_Auth_Exception
238: */
239: public function listUsers($sort = false)
240: {
241: /* Build a custom query, based on the config file. */
242: $query = str_replace(
243: '\L',
244: $this->_db->quote($this->_params['default_user']),
245: $this->_params['query_list']
246: );
247:
248: try {
249: $users = $this->_db->selectValues($query);
250: // Find a way to sort in database with portable SQL
251: return $this->_sort($users, $sort);
252: } catch (Horde_Db_Exception $e) {
253: throw new Horde_Auth_Exception($e);
254: }
255: }
256:
257: /**
258: * Checks if a userId exists in the system.
259: *
260: * @return boolean Whether or not the userId already exists.
261: */
262: public function exists($userId)
263: {
264: if (empty($this->_params['query_exists'])) {
265: return parent::exists($userId);
266: }
267:
268: /* Build a custom query, based on the config file. */
269: $query = str_replace(
270: '\L',
271: $this->_db->quote($userId),
272: $this->_params['query_exists']
273: );
274:
275: try {
276: return (bool)$this->_db->selectValue($query);
277: } catch (Horde_Db_Exception $e) {
278: return false;
279: }
280: }
281:
282: /**
283: * Fetch $userId's current password - needed for the salt with some
284: * encryption schemes when doing authentication or updates.
285: *
286: * @param string $userId The userId to query.
287: *
288: * @return string $userId's current password.
289: */
290: protected function _getPassword($userId)
291: {
292: /* Retrieve the old password in case we need the salt. */
293: $query = str_replace(
294: '\L',
295: $this->_db->quote($userId),
296: $this->_params['query_getpw']
297: );
298:
299: try {
300: return $this->_db->selectValue($query);
301: } catch (Horde_Db_Exception $e) {
302: return null;
303: }
304: }
305:
306: }
307: