Support Joomla!

Joomla! 1.5 Documentation

Packages

Package: OpenID

Developer Network License

The Joomla! Developer Network content is © copyright 2006 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution- NonCommercial- ShareAlike 2.5
Source code for file /openid/Auth/OpenID/SQLStore.php

Documentation is available at SQLStore.php

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

Documentation generated on Mon, 05 Mar 2007 21:26:49 +0000 by phpDocumentor 1.3.1