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