1<?php 2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 3 4/** 5 * A framework for authentication and authorization in PHP applications 6 * 7 * LiveUser is an authentication/permission framework designed 8 * to be flexible and easily extendable. 9 * 10 * Since it is impossible to have a 11 * "one size fits all" it takes a container 12 * approach which should enable it to 13 * be versatile enough to meet most needs. 14 * 15 * PHP version 4 and 5 16 * 17 * LICENSE: This library is free software; you can redistribute it and/or 18 * modify it under the terms of the GNU Lesser General Public 19 * License as published by the Free Software Foundation; either 20 * version 2.1 of the License, or (at your option) any later version. 21 * 22 * This library is distributed in the hope that it will be useful, 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 * Lesser General Public License for more details. 26 * 27 * You should have received a copy of the GNU Lesser General Public 28 * License along with this library; if not, write to the Free Software 29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 30 * MA 02111-1307 USA 31 * 32 * 33 * @category authentication 34 * @package LiveUser 35 * @author Markus Wolff <wolff@21st.de> 36 * @author Helgi �ormar �orbj�rnsson <dufuz@php.net> 37 * @author Lukas Smith <smith@pooteeweet.org> 38 * @author Arnaud Limbourg <arnaud@php.net> 39 * @author Pierre-Alain Joye <pajoye@php.net> 40 * @author Bjoern Kraus <krausbn@php.net> 41 * @copyright 2002-2006 Markus Wolff 42 * @license http://www.gnu.org/licenses/lgpl.txt 43 * @version CVS: $Id: PDO.php 304421 2010-10-15 13:30:56Z clockwerx $ 44 * @link http://pear.php.net/LiveUser 45 */ 46 47/** 48 * Require parent class definition. 49 */ 50require_once 'LiveUser/Perm/Storage/SQL.php'; 51 52/** 53 * PDO container for permission handling. 54 * 55 * This is a PDO backend driver for the LiveUser class. 56 * A PDO connection object can be passed to the constructor to reuse an 57 * existing connection. Alternatively, a DSN can be passed to open a new one. 58 * 59 * Requirements: 60 * - PHP5 61 * - File "Liveuser.php" (contains the parent class "LiveUser") 62 * - Array of connection options 63 * passed to the constructor. 64 * Example: array('dsn' => 'mysql:host:localhost;dbname=db_name', 65 * 'options' => array('username' => 'root', 'password' => 'secret', 'attr' => array())); 66 * 67 * @category authentication 68 * @package LiveUser 69 * @author Arnaud Limbourg <arnaud@php.net> 70 * @copyright 2002-2006 Markus Wolff 71 * @license http://www.gnu.org/licenses/lgpl.txt 72 * @version Release: @package_version@ 73 * @link http://pear.php.net/LiveUser 74 */ 75class LiveUser_Perm_Storage_PDO extends LiveUser_Perm_Storage_SQL 76{ 77 /** 78 * determines of the use of sequences should be forced 79 * 80 * @var bool 81 * @access private 82 */ 83 var $force_seq = true; 84 85 /** 86 * Initialize the storage container 87 * 88 * @param array Array with the storage configuration 89 * @return bool true on success, false on failure. 90 * 91 * @access public 92 */ 93 function init(&$storageConf) 94 { 95 parent::init($storageConf); 96 97 if (!is_a($this->dbc, 'pdo') && !is_null($this->dsn)) { 98 $login = $password = $extra = null; 99 if (!empty($this->options)) { 100 if (array_key_exists('username', $this->options)) { 101 $login = $this->options['username']; 102 } 103 if (array_key_exists('password', $this->options)) { 104 $password = $this->options['password']; 105 } 106 if (array_key_exists('attr', $this->options)) { 107 $extra = $this->options['attr']; 108 } 109 } 110 try { 111 $dbc = new PDO($this->dsn, $login, $password, $extra); 112 } catch (PDOException $e) { 113 $this->stack->push(LIVEUSER_ERROR_INIT_ERROR, 'error', 114 array( 115 'container' => 'could not connect: ' . $e->getMessage(), 116 'debug' => $e->getTrace() 117 ) 118 ); 119 return false; 120 } 121 $this->dbc = $dbc; 122 } 123 124 if (!is_a($this->dbc, 'pdo')) { 125 $this->stack->push(LIVEUSER_ERROR_INIT_ERROR, 'error', 126 array('container' => 'storage layer configuration missing')); 127 return false; 128 } 129 130 return true; 131 } 132 133 /** 134 * map an auth user to a perm user 135 * 136 * @param int auth user id 137 * @param string name of the container 138 * @return array|false requested data or false on failure 139 * 140 * @access public 141 */ 142 function mapUser($auth_user_id, $containerName) 143 { 144 $query = ' 145 SELECT 146 ' . $this->alias['perm_user_id'] . ' AS perm_user_id, 147 ' . $this->alias['perm_type'] . ' AS perm_type 148 FROM 149 '.$this->prefix.$this->alias['perm_users'].' 150 WHERE 151 ' . $this->alias['auth_user_id'] . ' = '. 152 $this->dbc->quote($auth_user_id).' 153 AND 154 ' . $this->alias['auth_container_name'] . ' = '. 155 $this->dbc->quote((string)$containerName); 156 157 $result = $this->dbc->query($query); 158 159 if ($result === false) { 160 $error_info = $this->dbc->errorInfo(); 161 $this->stack->push(LIVEUSER_ERROR, 'exception', array(), 162 'error in query ' . $error_info[2] . ' - ' . $query); 163 return false; 164 } 165 166 $row = $result->fetch(PDO::FETCH_ASSOC); 167 168 if ($row === false && $this->dbc->errorCode() === '00000') { 169 $row = null; 170 } 171 172 return $row; 173 } 174 175 /** 176 * Reads all rights of current user into a 177 * two-dimensional associative array, having the 178 * area names as the key of the 1st dimension. 179 * Group rights and invididual rights are being merged 180 * in the process. 181 * 182 * @param int perm user id 183 * @return array requested data or false on failure 184 * 185 * @access public 186 */ 187 function readUserRights($perm_user_id) 188 { 189 $query = ' 190 SELECT 191 ' . $this->alias['right_id'] . ', 192 ' . $this->alias['right_level'] . ' 193 FROM 194 '.$this->prefix.$this->alias['userrights'].' 195 WHERE 196 ' . $this->alias['perm_user_id'] . ' = '. 197 $this->dbc->quote($perm_user_id); 198 199 $result = $this->dbc->query($query); 200 201 if ($result === false) { 202 $error_info = $this->dbc->errorInfo(); 203 $this->stack->push(LIVEUSER_ERROR, 'exception', array(), 204 'error in query ' . $error_info[2] . ' - ' . $query); 205 return false; 206 } 207 208 $rows = array(); 209 while ($row = $result->fetch(PDO::FETCH_NUM)) { 210 $rows[$row[0]] = $row[1]; 211 } 212 213 return $rows; 214 } 215 216 /** 217 * read the areas in which a user is an area admin 218 * 219 * @param int perm user id 220 * @return array requested data or false on failure 221 * 222 * @access public 223 */ 224 function readAreaAdminAreas($perm_user_id) 225 { 226 // get all areas in which the user is area admin 227 $query = ' 228 SELECT 229 R.' . $this->alias['right_id'] . ' AS right_id, 230 '.LIVEUSER_MAX_LEVEL.' AS right_level 231 FROM 232 '.$this->prefix.$this->alias['area_admin_areas'].' AAA, 233 '.$this->prefix.$this->alias['rights'].' R 234 WHERE 235 AAA.area_id = R.area_id 236 AND 237 AAA.' . $this->alias['perm_user_id'] . ' = '. 238 $this->dbc->quote($perm_user_id); 239 240 $result = $this->dbc->query($query); 241 242 if ($result === false) { 243 $error_info = $this->dbc->errorInfo(); 244 $this->stack->push(LIVEUSER_ERROR, 'exception', array(), 245 'error in query ' . $error_info[2] . ' - ' . $query); 246 return false; 247 } 248 249 $rows = array(); 250 while ($row = $result->fetch(PDO::FETCH_NUM)) { 251 $rows[$row[0]] = $row[1]; 252 } 253 254 return $rows; 255 } 256 257 /** 258 * Reads all the group ids in that the user is also a member of 259 * (all groups that are subgroups of these are also added recursively) 260 * 261 * @param int perm user id 262 * @return array requested data or false on failure 263 * 264 * @see readRights() 265 * @access public 266 */ 267 function readGroups($perm_user_id) 268 { 269 $query = ' 270 SELECT 271 GU.' . $this->alias['group_id'] . ' 272 FROM 273 '.$this->prefix.$this->alias['groupusers'].' GU, 274 '.$this->prefix.$this->alias['groups'].' G 275 WHERE 276 GU.' . $this->alias['group_id'] . ' = G. ' . $this->alias['group_id'] . ' 277 AND 278 GU.' . $this->alias['perm_user_id'] . ' = '. 279 $this->dbc->quote($perm_user_id); 280 281 if (array_key_exists('is_active', $this->tables['groups']['fields'])) { 282 $query .= ' AND 283 G.' . $this->alias['is_active'] . '=' . 284 $this->dbc->quote(true); 285 } 286 287 $result = $this->dbc->query($query); 288 289 if ($result === false) { 290 $error_info = $this->dbc->errorInfo(); 291 $this->stack->push(LIVEUSER_ERROR, 'exception', array(), 292 'error in query ' . $error_info[2] . ' - ' . $query); 293 return false; 294 } 295 296 $col = $result->fetchAll(PDO::FETCH_COLUMN); 297 298 return $col; 299 } 300 301 /** 302 * Reads the group rights 303 * and put them in the array 304 * 305 * right => 1 306 * 307 * @param int group ids 308 * @return array requested data or false on failure 309 * 310 * @access public 311 */ 312 function readGroupRights($group_ids) 313 { 314 $query = ' 315 SELECT 316 GR.' . $this->alias['right_id'] . ', 317 MAX(GR.' . $this->alias['right_level'] . ') 318 FROM 319 '.$this->prefix.$this->alias['grouprights'].' GR 320 WHERE 321 GR.' . $this->alias['group_id'] . ' IN('. 322 implode(', ', $group_ids).') 323 GROUP BY 324 GR.' . $this->alias['right_id'] . ''; 325 326 $result = $this->dbc->query($query); 327 328 if ($result === false) { 329 $error_info = $this->dbc->errorInfo(); 330 $this->stack->push(LIVEUSER_ERROR, 'exception', array(), 331 'error in query ' . $error_info[2] . ' - ' . $query); 332 return false; 333 } 334 335 $rows = array(); 336 while ($row = $result->fetch(PDO::FETCH_NUM)) { 337 $rows[$row[0]] = $row[1]; 338 } 339 340 return $rows; 341 } 342 343 /** 344 * Read the sub groups of the new groups that are not part of the group ids 345 * 346 * @param array group ids 347 * @param array new group ids 348 * @return array requested data or false on failure 349 * 350 * @access public 351 */ 352 function readSubGroups($group_ids, $newGroupIds) 353 { 354 $query = ' 355 SELECT 356 DISTINCT SG.' . $this->alias['subgroup_id'] . ' 357 FROM 358 '.$this->prefix.$this->alias['groups'].' G, 359 '.$this->prefix.$this->alias['group_subgroups'].' SG 360 WHERE 361 SG.' . $this->alias['subgroup_id'] . ' = G.' . 362 $this->alias['group_id'] . ' 363 AND 364 SG.' . $this->alias['group_id'] . ' IN ('. 365 implode(', ', $newGroupIds).') 366 AND 367 SG.' . $this->alias['subgroup_id'] . ' NOT IN ('. 368 implode(', ', $group_ids).')'; 369 370 if (array_key_exists('is_active', $this->tables['groups']['fields'])) { 371 $query .= ' AND 372 G.' . $this->alias['is_active'] . '=' . 373 $this->dbc->quote(true); 374 } 375 376 $result = $this->dbc->query($query); 377 378 if ($result === false) { 379 $error_info = $this->dbc->errorInfo(); 380 $this->stack->push(LIVEUSER_ERROR, 'exception', array(), 381 'error in query ' . $error_info[2] . ' - ' . $query); 382 return false; 383 } 384 385 $col = $result->fetchAll(PDO::FETCH_COLUMN); 386 387 return $col; 388 } 389 390 /** 391 * Read out the rights from the userrights or grouprights table 392 * that imply other rights along with their level 393 * 394 * @param array right ids 395 * @param string name of the table 396 * @return array requested data or false on failure 397 * 398 * @access public 399 */ 400 function readImplyingRights($rightIds, $table) 401 { 402 $query = ' 403 SELECT 404 DISTINCT 405 TR.' . $this->alias['right_level'] . ', 406 TR.' . $this->alias['right_id'] . ' 407 FROM 408 '.$this->prefix.$this->alias['rights'].' R, 409 '.$this->prefix.$this->alias[$table.'rights'].' TR 410 WHERE 411 TR.' . $this->alias['right_id'] . ' = R.' . $this->alias['right_id'] . ' 412 AND 413 R.' . $this->alias['right_id'] . ' IN ('. 414 implode(', ', array_keys($rightIds)).') 415 AND 416 R.' . $this->alias['has_implied'] . '='. 417 $this->dbc->quote(true); 418 419 $result = $this->dbc->query($query); 420 421 if ($result === false) { 422 $error_info = $this->dbc->errorInfo(); 423 $this->stack->push(LIVEUSER_ERROR, 'exception', array(), 424 'error in query ' . $error_info[2] . ' - ' . $query); 425 return false; 426 } 427 428 $rows = array(); 429 while ($row = $result->fetch(PDO::FETCH_NUM)) { 430 $rows[$row[0]][] = $row[1]; 431 } 432 433 return $rows; 434 } 435 436 /** 437 * Read out the implied rights with a given level from the implied_rights table 438 * 439 * @param array current right ids 440 * @param string current level 441 * @return array requested data or false on failure 442 * 443 * @access public 444 */ 445 function readImpliedRights($currentRights, $currentLevel) 446 { 447 $query = ' 448 SELECT 449 RI.' . $this->alias['implied_right_id'] . ' AS right_id, 450 '.$currentLevel.' AS right_level, 451 R.' . $this->alias['has_implied'] . ' AS has_implied 452 FROM 453 '.$this->prefix.$this->alias['rights'].' R, 454 '.$this->prefix.$this->alias['right_implied'].' RI 455 WHERE 456 RI.' . $this->alias['implied_right_id'] . ' = R.' . $this->alias['right_id'] . ' 457 AND 458 RI.' . $this->alias['right_id'] . ' IN ('. 459 implode(', ', $currentRights).')'; 460 461 $result = $this->dbc->query($query); 462 463 if ($result === false) { 464 $error_info = $this->dbc->errorInfo(); 465 $this->stack->push(LIVEUSER_ERROR, 'exception', array(), 466 'error in query ' . $error_info[2] . ' - ' . $query); 467 return false; 468 } 469 470 $rows = $result->fetchAll(PDO::FETCH_ASSOC); 471 472 return (array)$rows; 473 } 474 475 /** 476 * Override the disconnect method from the parent class. 477 * 478 * @return void 479 */ 480 function disconnect() 481 { 482 if ($this->dsn) { 483 $this->dbc = null; 484 } 485 } 486} 487?> 488