[ Index ]

PHP Cross Reference of Joomla 1.5.26 DE

title

Body

[close]

/libraries/openid/Auth/OpenID/ -> SQLStore.php (source)

   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  ?>


Generated: Wed Mar 28 15:54:07 2012 Cross-referenced by PHPXref 0.7.1