| [ Index ] |
PHP Cross Reference of Joomla 1.5.26 DE |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * SQL-backed OpenID stores. 5 * 6 * PHP versions 4 and 5 7 * 8 * LICENSE: See the COPYING file included in this distribution. 9 * 10 * @package OpenID 11 * @author JanRain, Inc. <openid@janrain.com> 12 * @copyright 2005-2008 Janrain, Inc. 13 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache 14 */ 15 16 // Do not allow direct access 17 defined( '_JEXEC' ) or die( 'Restricted access' ); 18 19 /** 20 * Require the PEAR DB module because we'll need it for the SQL-based 21 * stores implemented here. We silence any errors from the inclusion 22 * because it might not be present, and a user of the SQL stores may 23 * supply an Auth_OpenID_DatabaseConnection instance that implements 24 * its own storage. 25 */ 26 global $__Auth_OpenID_PEAR_AVAILABLE; 27 $__Auth_OpenID_PEAR_AVAILABLE = @include_once 'DB.php'; 28 29 /** 30 * @access private 31 */ 32 require_once 'Auth/OpenID/Interface.php'; 33 require_once 'Auth/OpenID/Nonce.php'; 34 35 /** 36 * @access private 37 */ 38 require_once 'Auth/OpenID.php'; 39 40 /** 41 * @access private 42 */ 43 require_once 'Auth/OpenID/Nonce.php'; 44 45 /** 46 * This is the parent class for the SQL stores, which contains the 47 * logic common to all of the SQL stores. 48 * 49 * The table names used are determined by the class variables 50 * associations_table_name and nonces_table_name. To change the name 51 * of the tables used, pass new table names into the constructor. 52 * 53 * To create the tables with the proper schema, see the createTables 54 * method. 55 * 56 * This class shouldn't be used directly. Use one of its subclasses 57 * instead, as those contain the code necessary to use a specific 58 * database. If you're an OpenID integrator and you'd like to create 59 * an SQL-driven store that wraps an application's database 60 * abstraction, be sure to create a subclass of 61 * {@link Auth_OpenID_DatabaseConnection} that calls the application's 62 * database abstraction calls. Then, pass an instance of your new 63 * database connection class to your SQLStore subclass constructor. 64 * 65 * All methods other than the constructor and createTables should be 66 * considered implementation details. 67 * 68 * @package OpenID 69 */ 70 class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore { 71 72 /** 73 * This creates a new SQLStore instance. It requires an 74 * established database connection be given to it, and it allows 75 * overriding the default table names. 76 * 77 * @param connection $connection This must be an established 78 * connection to a database of the correct type for the SQLStore 79 * subclass you're using. This must either be an PEAR DB 80 * connection handle or an instance of a subclass of 81 * Auth_OpenID_DatabaseConnection. 82 * 83 * @param associations_table: This is an optional parameter to 84 * specify the name of the table used for storing associations. 85 * The default value is 'oid_associations'. 86 * 87 * @param nonces_table: This is an optional parameter to specify 88 * the name of the table used for storing nonces. The default 89 * value is 'oid_nonces'. 90 */ 91 function Auth_OpenID_SQLStore($connection, 92 $associations_table = null, 93 $nonces_table = null) 94 { 95 global $__Auth_OpenID_PEAR_AVAILABLE; 96 97 $this->associations_table_name = "oid_associations"; 98 $this->nonces_table_name = "oid_nonces"; 99 100 // Check the connection object type to be sure it's a PEAR 101 // database connection. 102 if (!(is_object($connection) && 103 (is_subclass_of($connection, 'db_common') || 104 is_subclass_of($connection, 105 'auth_openid_databaseconnection')))) { 106 trigger_error("Auth_OpenID_SQLStore expected PEAR connection " . 107 "object (got ".get_class($connection).")", 108 E_USER_ERROR); 109 return; 110 } 111 112 $this->connection = $connection; 113 114 // Be sure to set the fetch mode so the results are keyed on 115 // column name instead of column index. This is a PEAR 116 // constant, so only try to use it if PEAR is present. Note 117 // that Auth_Openid_Databaseconnection instances need not 118 // implement ::setFetchMode for this reason. 119 if ($__Auth_OpenID_PEAR_AVAILABLE) { 120 $this->connection->setFetchMode(DB_FETCHMODE_ASSOC); 121 } 122 123 if ($associations_table) { 124 $this->associations_table_name = $associations_table; 125 } 126 127 if ($nonces_table) { 128 $this->nonces_table_name = $nonces_table; 129 } 130 131 $this->max_nonce_age = 6 * 60 * 60; 132 133 // Be sure to run the database queries with auto-commit mode 134 // turned OFF, because we want every function to run in a 135 // transaction, implicitly. As a rule, methods named with a 136 // leading underscore will NOT control transaction behavior. 137 // Callers of these methods will worry about transactions. 138 $this->connection->autoCommit(false); 139 140 // Create an empty SQL strings array. 141 $this->sql = array(); 142 143 // Call this method (which should be overridden by subclasses) 144 // to populate the $this->sql array with SQL strings. 145 $this->setSQL(); 146 147 // Verify that all required SQL statements have been set, and 148 // raise an error if any expected SQL strings were either 149 // absent or empty. 150 list($missing, $empty) = $this->_verifySQL(); 151 152 if ($missing) { 153 trigger_error("Expected keys in SQL query list: " . 154 implode(", ", $missing), 155 E_USER_ERROR); 156 return; 157 } 158 159 if ($empty) { 160 trigger_error("SQL list keys have no SQL strings: " . 161 implode(", ", $empty), 162 E_USER_ERROR); 163 return; 164 } 165 166 // Add table names to queries. 167 $this->_fixSQL(); 168 } 169 170 function tableExists($table_name) 171 { 172 return !$this->isError( 173 $this->connection->query( 174 sprintf("SELECT * FROM %s LIMIT 0", 175 $table_name))); 176 } 177 178 /** 179 * Returns true if $value constitutes a database error; returns 180 * false otherwise. 181 */ 182 function isError($value) 183 { 184 return PEAR::isError($value); 185 } 186 187 /** 188 * Converts a query result to a boolean. If the result is a 189 * database error according to $this->isError(), this returns 190 * false; otherwise, this returns true. 191 */ 192 function resultToBool($obj) 193 { 194 if ($this->isError($obj)) { 195 return false; 196 } else { 197 return true; 198 } 199 } 200 201 /** 202 * This method should be overridden by subclasses. This method is 203 * called by the constructor to set values in $this->sql, which is 204 * an array keyed on sql name. 205 */ 206 function setSQL() 207 { 208 } 209 210 /** 211 * Resets the store by removing all records from the store's 212 * tables. 213 */ 214 function reset() 215 { 216 $this->connection->query(sprintf("DELETE FROM %s", 217 $this->associations_table_name)); 218 219 $this->connection->query(sprintf("DELETE FROM %s", 220 $this->nonces_table_name)); 221 } 222 223 /** 224 * @access private 225 */ 226 function _verifySQL() 227 { 228 $missing = array(); 229 $empty = array(); 230 231 $required_sql_keys = array( 232 'nonce_table', 233 'assoc_table', 234 'set_assoc', 235 'get_assoc', 236 'get_assocs', 237 'remove_assoc' 238 ); 239 240 foreach ($required_sql_keys as $key) { 241 if (!array_key_exists($key, $this->sql)) { 242 $missing[] = $key; 243 } else if (!$this->sql[$key]) { 244 $empty[] = $key; 245 } 246 } 247 248 return array($missing, $empty); 249 } 250 251 /** 252 * @access private 253 */ 254 function _fixSQL() 255 { 256 $replacements = array( 257 array( 258 'value' => $this->nonces_table_name, 259 'keys' => array('nonce_table', 260 'add_nonce', 261 'clean_nonce') 262 ), 263 array( 264 'value' => $this->associations_table_name, 265 'keys' => array('assoc_table', 266 'set_assoc', 267 'get_assoc', 268 'get_assocs', 269 'remove_assoc', 270 'clean_assoc') 271 ) 272 ); 273 274 foreach ($replacements as $item) { 275 $value = $item['value']; 276 $keys = $item['keys']; 277 278 foreach ($keys as $k) { 279 if (is_array($this->sql[$k])) { 280 foreach ($this->sql[$k] as $part_key => $part_value) { 281 $this->sql[$k][$part_key] = sprintf($part_value, 282 $value); 283 } 284 } else { 285 $this->sql[$k] = sprintf($this->sql[$k], $value); 286 } 287 } 288 } 289 } 290 291 function blobDecode($blob) 292 { 293 return $blob; 294 } 295 296 function blobEncode($str) 297 { 298 return $str; 299 } 300 301 function createTables() 302 { 303 $this->connection->autoCommit(true); 304 $n = $this->create_nonce_table(); 305 $a = $this->create_assoc_table(); 306 $this->connection->autoCommit(false); 307 308 if ($n && $a) { 309 return true; 310 } else { 311 return false; 312 } 313 } 314 315 function create_nonce_table() 316 { 317 if (!$this->tableExists($this->nonces_table_name)) { 318 $r = $this->connection->query($this->sql['nonce_table']); 319 return $this->resultToBool($r); 320 } 321 return true; 322 } 323 324 function create_assoc_table() 325 { 326 if (!$this->tableExists($this->associations_table_name)) { 327 $r = $this->connection->query($this->sql['assoc_table']); 328 return $this->resultToBool($r); 329 } 330 return true; 331 } 332 333 /** 334 * @access private 335 */ 336 function _set_assoc($server_url, $handle, $secret, $issued, 337 $lifetime, $assoc_type) 338 { 339 return $this->connection->query($this->sql['set_assoc'], 340 array( 341 $server_url, 342 $handle, 343 $secret, 344 $issued, 345 $lifetime, 346 $assoc_type)); 347 } 348 349 function storeAssociation($server_url, $association) 350 { 351 if ($this->resultToBool($this->_set_assoc( 352 $server_url, 353 $association->handle, 354 $this->blobEncode( 355 $association->secret), 356 $association->issued, 357 $association->lifetime, 358 $association->assoc_type 359 ))) { 360 $this->connection->commit(); 361 } else { 362 $this->connection->rollback(); 363 } 364 } 365 366 /** 367 * @access private 368 */ 369 function _get_assoc($server_url, $handle) 370 { 371 $result = $this->connection->getRow($this->sql['get_assoc'], 372 array($server_url, $handle)); 373 if ($this->isError($result)) { 374 return null; 375 } else { 376 return $result; 377 } 378 } 379 380 /** 381 * @access private 382 */ 383 function _get_assocs($server_url) 384 { 385 $result = $this->connection->getAll($this->sql['get_assocs'], 386 array($server_url)); 387 388 if ($this->isError($result)) { 389 return array(); 390 } else { 391 return $result; 392 } 393 } 394 395 function removeAssociation($server_url, $handle) 396 { 397 if ($this->_get_assoc($server_url, $handle) == null) { 398 return false; 399 } 400 401 if ($this->resultToBool($this->connection->query( 402 $this->sql['remove_assoc'], 403 array($server_url, $handle)))) { 404 $this->connection->commit(); 405 } else { 406 $this->connection->rollback(); 407 } 408 409 return true; 410 } 411 412 function getAssociation($server_url, $handle = null) 413 { 414 if ($handle !== null) { 415 $assoc = $this->_get_assoc($server_url, $handle); 416 417 $assocs = array(); 418 if ($assoc) { 419 $assocs[] = $assoc; 420 } 421 } else { 422 $assocs = $this->_get_assocs($server_url); 423 } 424 425 if (!$assocs || (count($assocs) == 0)) { 426 return null; 427 } else { 428 $associations = array(); 429 430 foreach ($assocs as $assoc_row) { 431 $assoc = new Auth_OpenID_Association($assoc_row['handle'], 432 $assoc_row['secret'], 433 $assoc_row['issued'], 434 $assoc_row['lifetime'], 435 $assoc_row['assoc_type']); 436 437 $assoc->secret = $this->blobDecode($assoc->secret); 438 439 if ($assoc->getExpiresIn() == 0) { 440 $this->removeAssociation($server_url, $assoc->handle); 441 } else { 442 $associations[] = array($assoc->issued, $assoc); 443 } 444 } 445 446 if ($associations) { 447 $issued = array(); 448 $assocs = array(); 449 foreach ($associations as $key => $assoc) { 450 $issued[$key] = $assoc[0]; 451 $assocs[$key] = $assoc[1]; 452 } 453 454 array_multisort($issued, SORT_DESC, $assocs, SORT_DESC, 455 $associations); 456 457 // return the most recently issued one. 458 list($issued, $assoc) = $associations[0]; 459 return $assoc; 460 } else { 461 return null; 462 } 463 } 464 } 465 466 /** 467 * @access private 468 */ 469 function _add_nonce($server_url, $timestamp, $salt) 470 { 471 $sql = $this->sql['add_nonce']; 472 $result = $this->connection->query($sql, array($server_url, 473 $timestamp, 474 $salt)); 475 if ($this->isError($result)) { 476 $this->connection->rollback(); 477 } else { 478 $this->connection->commit(); 479 } 480 return $this->resultToBool($result); 481 } 482 483 function useNonce($server_url, $timestamp, $salt) 484 { 485 global $Auth_OpenID_SKEW; 486 487 if ( abs($timestamp - time()) > $Auth_OpenID_SKEW ) { 488 return False; 489 } 490 491 return $this->_add_nonce($server_url, $timestamp, $salt); 492 } 493 494 /** 495 * "Octifies" a binary string by returning a string with escaped 496 * octal bytes. This is used for preparing binary data for 497 * PostgreSQL BYTEA fields. 498 * 499 * @access private 500 */ 501 function _octify($str) 502 { 503 $result = ""; 504 for ($i = 0; $i < Auth_OpenID::bytes($str); $i++) { 505 $ch = substr($str, $i, 1); 506 if ($ch == "\\") { 507 $result .= "\\\\\\\\"; 508 } else if (ord($ch) == 0) { 509 $result .= "\\\\000"; 510 } else { 511 $result .= "\\" . strval(decoct(ord($ch))); 512 } 513 } 514 return $result; 515 } 516 517 /** 518 * "Unoctifies" octal-escaped data from PostgreSQL and returns the 519 * resulting ASCII (possibly binary) string. 520 * 521 * @access private 522 */ 523 function _unoctify($str) 524 { 525 $result = ""; 526 $i = 0; 527 while ($i < strlen($str)) { 528 $char = $str[$i]; 529 if ($char == "\\") { 530 // Look to see if the next char is a backslash and 531 // append it. 532 if ($str[$i + 1] != "\\") { 533 $octal_digits = substr($str, $i + 1, 3); 534 $dec = octdec($octal_digits); 535 $char = chr($dec); 536 $i += 4; 537 } else { 538 $char = "\\"; 539 $i += 2; 540 } 541 } else { 542 $i += 1; 543 } 544 545 $result .= $char; 546 } 547 548 return $result; 549 } 550 551 function cleanupNonces() 552 { 553 global $Auth_OpenID_SKEW; 554 $v = time() - $Auth_OpenID_SKEW; 555 556 $this->connection->query($this->sql['clean_nonce'], array($v)); 557 $num = $this->connection->affectedRows(); 558 $this->connection->commit(); 559 return $num; 560 } 561 562 function cleanupAssociations() 563 { 564 $this->connection->query($this->sql['clean_assoc'], 565 array(time())); 566 $num = $this->connection->affectedRows(); 567 $this->connection->commit(); 568 return $num; 569 } 570 } 571 572 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Mar 28 15:54:07 2012 | Cross-referenced by PHPXref 0.7.1 |