414 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			414 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
|   | <?php | ||
|  | 
 | ||
|  | /** | ||
|  |  * SQL-backed OpenID stores for use with PEAR::MDB2. | ||
|  |  * | ||
|  |  * PHP versions 4 and 5 | ||
|  |  * | ||
|  |  * LICENSE: See the COPYING file included in this distribution. | ||
|  |  * | ||
|  |  * @package OpenID | ||
|  |  * @author JanRain, Inc. <openid@janrain.com> | ||
|  |  * @copyright 2005 Janrain, Inc. | ||
|  |  * @license http://www.gnu.org/copyleft/lesser.html LGPL | ||
|  |  */ | ||
|  | 
 | ||
|  | require_once 'MDB2.php'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * @access private | ||
|  |  */ | ||
|  | require_once 'Auth/OpenID/Interface.php'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * @access private | ||
|  |  */ | ||
|  | require_once 'Auth/OpenID.php'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * @access private | ||
|  |  */ | ||
|  | require_once 'Auth/OpenID/Nonce.php'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * This store uses a PEAR::MDB2 connection to store persistence | ||
|  |  * information. | ||
|  |  * | ||
|  |  * The table names used are determined by the class variables | ||
|  |  * associations_table_name and nonces_table_name.  To change the name | ||
|  |  * of the tables used, pass new table names into the constructor. | ||
|  |  * | ||
|  |  * To create the tables with the proper schema, see the createTables | ||
|  |  * method. | ||
|  |  * | ||
|  |  * @package OpenID | ||
|  |  */ | ||
|  | class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore { | ||
|  |     /** | ||
|  |      * This creates a new MDB2Store instance.  It requires an | ||
|  |      * established database connection be given to it, and it allows | ||
|  |      * overriding the default table names. | ||
|  |      * | ||
|  |      * @param connection $connection This must be an established | ||
|  |      * connection to a database of the correct type for the SQLStore | ||
|  |      * subclass you're using.  This must be a PEAR::MDB2 connection | ||
|  |      * handle. | ||
|  |      * | ||
|  |      * @param associations_table: This is an optional parameter to | ||
|  |      * specify the name of the table used for storing associations. | ||
|  |      * The default value is 'oid_associations'. | ||
|  |      * | ||
|  |      * @param nonces_table: This is an optional parameter to specify | ||
|  |      * the name of the table used for storing nonces.  The default | ||
|  |      * value is 'oid_nonces'. | ||
|  |      */ | ||
|  |     function Auth_OpenID_MDB2Store($connection, | ||
|  |                                   $associations_table = null, | ||
|  |                                   $nonces_table = null) | ||
|  |     { | ||
|  |         $this->associations_table_name = "oid_associations"; | ||
|  |         $this->nonces_table_name = "oid_nonces"; | ||
|  | 
 | ||
|  |         // Check the connection object type to be sure it's a PEAR
 | ||
|  |         // database connection.
 | ||
|  |         if (!is_object($connection) || | ||
|  |             !is_subclass_of($connection, 'mdb2_driver_common')) { | ||
|  |             trigger_error("Auth_OpenID_MDB2Store expected PEAR connection " . | ||
|  |                           "object (got ".get_class($connection).")", | ||
|  |                           E_USER_ERROR); | ||
|  |             return; | ||
|  |         } | ||
|  | 
 | ||
|  |         $this->connection = $connection; | ||
|  | 
 | ||
|  |         // Be sure to set the fetch mode so the results are keyed on
 | ||
|  |         // column name instead of column index.
 | ||
|  |         $this->connection->setFetchMode(MDB2_FETCHMODE_ASSOC); | ||
|  |          | ||
|  |         if (@PEAR::isError($this->connection->loadModule('Extended'))) { | ||
|  |             trigger_error("Unable to load MDB2_Extended module", E_USER_ERROR); | ||
|  |             return; | ||
|  |         } | ||
|  | 
 | ||
|  |         if ($associations_table) { | ||
|  |             $this->associations_table_name = $associations_table; | ||
|  |         } | ||
|  | 
 | ||
|  |         if ($nonces_table) { | ||
|  |             $this->nonces_table_name = $nonces_table; | ||
|  |         } | ||
|  | 
 | ||
|  |         $this->max_nonce_age = 6 * 60 * 60; | ||
|  |     } | ||
|  | 
 | ||
|  |     function tableExists($table_name) | ||
|  |     { | ||
|  |         return !@PEAR::isError($this->connection->query( | ||
|  |                                   sprintf("SELECT * FROM %s LIMIT 0", | ||
|  |                                           $table_name))); | ||
|  |     } | ||
|  | 
 | ||
|  |     function createTables() | ||
|  |     { | ||
|  |         $n = $this->create_nonce_table(); | ||
|  |         $a = $this->create_assoc_table(); | ||
|  | 
 | ||
|  |         if (!$n || !$a) { | ||
|  |             return false; | ||
|  |         } | ||
|  |         return true; | ||
|  |     } | ||
|  | 
 | ||
|  |     function create_nonce_table() | ||
|  |     { | ||
|  |         if (!$this->tableExists($this->nonces_table_name)) { | ||
|  |             switch ($this->connection->phptype) { | ||
|  |                 case "mysql": | ||
|  |                 case "mysqli": | ||
|  |                     // Custom SQL for MySQL to use InnoDB and variable-
 | ||
|  |                     // length keys
 | ||
|  |                     $r = $this->connection->exec( | ||
|  |                         sprintf("CREATE TABLE %s (\n". | ||
|  |                                 "  server_url VARCHAR(2047) NOT NULL DEFAULT '',\n". | ||
|  |                                 "  timestamp INTEGER NOT NULL,\n". | ||
|  |                                 "  salt CHAR(40) NOT NULL,\n". | ||
|  |                                 "  UNIQUE (server_url(255), timestamp, salt)\n". | ||
|  |                                 ") TYPE=InnoDB", | ||
|  |                                 $this->nonces_table_name)); | ||
|  |                     if (@PEAR::isError($r)) { | ||
|  |                         return false; | ||
|  |                     } | ||
|  |                     break; | ||
|  |                 default: | ||
|  |                     if (@PEAR::isError( | ||
|  |                         $this->connection->loadModule('Manager'))) { | ||
|  |                         return false; | ||
|  |                     } | ||
|  |                     $fields = array( | ||
|  |                         "server_url" => array( | ||
|  |                             "type" => "text", | ||
|  |                             "length" => 2047, | ||
|  |                             "notnull" => true | ||
|  |                         ), | ||
|  |                         "timestamp" => array( | ||
|  |                             "type" => "integer", | ||
|  |                             "notnull" => true | ||
|  |                         ), | ||
|  |                         "salt" => array( | ||
|  |                             "type" => "text", | ||
|  |                             "length" => 40, | ||
|  |                             "fixed" => true, | ||
|  |                             "notnull" => true | ||
|  |                         ) | ||
|  |                     ); | ||
|  |                     $constraint = array( | ||
|  |                         "unique" => 1, | ||
|  |                         "fields" => array( | ||
|  |                             "server_url" => true, | ||
|  |                             "timestamp" => true, | ||
|  |                             "salt" => true | ||
|  |                         ) | ||
|  |                     ); | ||
|  |                      | ||
|  |                     $r = $this->connection->createTable($this->nonces_table_name, | ||
|  |                                                         $fields); | ||
|  |                     if (@PEAR::isError($r)) { | ||
|  |                         return false; | ||
|  |                     } | ||
|  |                      | ||
|  |                     $r = $this->connection->createConstraint( | ||
|  |                         $this->nonces_table_name, | ||
|  |                         $this->nonces_table_name . "_constraint", | ||
|  |                         $constraint); | ||
|  |                     if (@PEAR::isError($r)) { | ||
|  |                         return false; | ||
|  |                     } | ||
|  |                     break; | ||
|  |             } | ||
|  |         } | ||
|  |         return true; | ||
|  |     } | ||
|  | 
 | ||
|  |     function create_assoc_table() | ||
|  |     { | ||
|  |         if (!$this->tableExists($this->associations_table_name)) { | ||
|  |             switch ($this->connection->phptype) { | ||
|  |                 case "mysql": | ||
|  |                 case "mysqli": | ||
|  |                     // Custom SQL for MySQL to use InnoDB and variable-
 | ||
|  |                     // length keys
 | ||
|  |                     $r = $this->connection->exec( | ||
|  |                         sprintf("CREATE TABLE %s(\n". | ||
|  |                                 "  server_url VARCHAR(2047) NOT NULL DEFAULT '',\n". | ||
|  |                                 "  handle VARCHAR(255) NOT NULL,\n". | ||
|  |                                 "  secret BLOB NOT NULL,\n". | ||
|  |                                 "  issued INTEGER NOT NULL,\n". | ||
|  |                                 "  lifetime INTEGER NOT NULL,\n". | ||
|  |                                 "  assoc_type VARCHAR(64) NOT NULL,\n". | ||
|  |                                 "  PRIMARY KEY (server_url(255), handle)\n". | ||
|  |                                 ") TYPE=InnoDB", | ||
|  |                             $this->associations_table_name)); | ||
|  |                     if (@PEAR::isError($r)) { | ||
|  |                         return false; | ||
|  |                     } | ||
|  |                     break; | ||
|  |                 default: | ||
|  |                     if (@PEAR::isError( | ||
|  |                         $this->connection->loadModule('Manager'))) { | ||
|  |                         return false; | ||
|  |                     } | ||
|  |                     $fields = array( | ||
|  |                         "server_url" => array( | ||
|  |                             "type" => "text", | ||
|  |                             "length" => 2047, | ||
|  |                             "notnull" => true | ||
|  |                         ), | ||
|  |                         "handle" => array( | ||
|  |                             "type" => "text", | ||
|  |                             "length" => 255, | ||
|  |                             "notnull" => true | ||
|  |                         ), | ||
|  |                         "secret" => array( | ||
|  |                             "type" => "blob", | ||
|  |                             "length" => "255", | ||
|  |                             "notnull" => true | ||
|  |                         ), | ||
|  |                         "issued" => array( | ||
|  |                             "type" => "integer", | ||
|  |                             "notnull" => true | ||
|  |                         ), | ||
|  |                         "lifetime" => array( | ||
|  |                             "type" => "integer", | ||
|  |                             "notnull" => true | ||
|  |                         ), | ||
|  |                         "assoc_type" => array( | ||
|  |                             "type" => "text", | ||
|  |                             "length" => 64, | ||
|  |                             "notnull" => true | ||
|  |                         ) | ||
|  |                     ); | ||
|  |                     $options = array( | ||
|  |                         "primary" => array( | ||
|  |                             "server_url" => true, | ||
|  |                             "handle" => true | ||
|  |                         ) | ||
|  |                     ); | ||
|  |                      | ||
|  |                     $r = $this->connection->createTable( | ||
|  |                         $this->associations_table_name, | ||
|  |                         $fields, | ||
|  |                         $options); | ||
|  |                     if (@PEAR::isError($r)) { | ||
|  |                         return false; | ||
|  |                     } | ||
|  |                     break; | ||
|  |             } | ||
|  |         } | ||
|  |         return true; | ||
|  |     } | ||
|  | 
 | ||
|  |     function storeAssociation($server_url, $association) | ||
|  |     { | ||
|  |         $fields = array( | ||
|  |             "server_url" => array( | ||
|  |                 "value" => $server_url, | ||
|  |                 "key" => true | ||
|  |             ), | ||
|  |             "handle" => array( | ||
|  |                 "value" => $association->handle, | ||
|  |                 "key" => true | ||
|  |             ), | ||
|  |             "secret" => array( | ||
|  |                 "value" => $association->secret, | ||
|  |                 "type" => "blob" | ||
|  |             ), | ||
|  |             "issued" => array( | ||
|  |                 "value" => $association->issued | ||
|  |             ), | ||
|  |             "lifetime" => array( | ||
|  |                 "value" => $association->lifetime | ||
|  |             ), | ||
|  |             "assoc_type" => array( | ||
|  |                 "value" => $association->assoc_type | ||
|  |             ) | ||
|  |         ); | ||
|  |          | ||
|  |         return !@PEAR::isError($this->connection->replace( | ||
|  |                                   $this->associations_table_name, | ||
|  |                                   $fields)); | ||
|  |     } | ||
|  | 
 | ||
|  |     function cleanupNonces() | ||
|  |     { | ||
|  |         global $Auth_OpenID_SKEW; | ||
|  |         $v = time() - $Auth_OpenID_SKEW; | ||
|  | 
 | ||
|  |         return $this->connection->exec( | ||
|  |             sprintf("DELETE FROM %s WHERE timestamp < %d", | ||
|  |                     $this->nonces_table_name, $v)); | ||
|  |     } | ||
|  | 
 | ||
|  |     function cleanupAssociations() | ||
|  |     { | ||
|  |         return $this->connection->exec( | ||
|  |             sprintf("DELETE FROM %s WHERE issued + lifetime < %d", | ||
|  |                     $this->associations_table_name, time())); | ||
|  |     } | ||
|  | 
 | ||
|  |     function getAssociation($server_url, $handle = null) | ||
|  |     { | ||
|  |         $sql = ""; | ||
|  |         $params = null; | ||
|  |         $types = array( | ||
|  |                        "text", | ||
|  |                        "blob", | ||
|  |                        "integer", | ||
|  |                        "integer", | ||
|  |                        "text" | ||
|  |                        ); | ||
|  |         if ($handle !== null) { | ||
|  |             $sql = sprintf("SELECT handle, secret, issued, lifetime, assoc_type " . | ||
|  |                            "FROM %s WHERE server_url = ? AND handle = ?", | ||
|  |                            $this->associations_table_name); | ||
|  |             $params = array($server_url, $handle); | ||
|  |         } else { | ||
|  |             $sql = sprintf("SELECT handle, secret, issued, lifetime, assoc_type " . | ||
|  |                            "FROM %s WHERE server_url = ? ORDER BY issued DESC", | ||
|  |                            $this->associations_table_name); | ||
|  |             $params = array($server_url); | ||
|  |         } | ||
|  |          | ||
|  |         $assoc = $this->connection->getRow($sql, $types, $params); | ||
|  | 
 | ||
|  |         if (!$assoc || @PEAR::isError($assoc)) { | ||
|  |             return null; | ||
|  |         } else { | ||
|  |             $association = new Auth_OpenID_Association($assoc['handle'], | ||
|  |                                                        stream_get_contents( | ||
|  |                                                            $assoc['secret']), | ||
|  |                                                        $assoc['issued'], | ||
|  |                                                        $assoc['lifetime'], | ||
|  |                                                        $assoc['assoc_type']); | ||
|  |             fclose($assoc['secret']); | ||
|  |             return $association; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     function removeAssociation($server_url, $handle) | ||
|  |     { | ||
|  |         $r = $this->connection->execParam( | ||
|  |             sprintf("DELETE FROM %s WHERE server_url = ? AND handle = ?", | ||
|  |                     $this->associations_table_name), | ||
|  |             array($server_url, $handle)); | ||
|  |          | ||
|  |         if (@PEAR::isError($r) || $r == 0) { | ||
|  |             return false; | ||
|  |         } | ||
|  |         return true; | ||
|  |     } | ||
|  | 
 | ||
|  |     function useNonce($server_url, $timestamp, $salt) | ||
|  |     { | ||
|  |         global $Auth_OpenID_SKEW; | ||
|  | 
 | ||
|  |         if (abs($timestamp - time()) > $Auth_OpenID_SKEW ) { | ||
|  |             return false; | ||
|  |         } | ||
|  |          | ||
|  |         $fields = array( | ||
|  |                         "timestamp" => $timestamp, | ||
|  |                         "salt" => $salt | ||
|  |                         ); | ||
|  |          | ||
|  |         if (!empty($server_url)) { | ||
|  |             $fields["server_url"] = $server_url; | ||
|  |         } | ||
|  |          | ||
|  |         $r = $this->connection->autoExecute( | ||
|  |             $this->nonces_table_name, | ||
|  |             $fields, | ||
|  |             MDB2_AUTOQUERY_INSERT); | ||
|  |          | ||
|  |         if (@PEAR::isError($r)) { | ||
|  |             return false; | ||
|  |         } | ||
|  |         return true; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Resets the store by removing all records from the store's | ||
|  |      * tables. | ||
|  |      */ | ||
|  |     function reset() | ||
|  |     { | ||
|  |         $this->connection->query(sprintf("DELETE FROM %s", | ||
|  |                                          $this->associations_table_name)); | ||
|  | 
 | ||
|  |         $this->connection->query(sprintf("DELETE FROM %s", | ||
|  |                                          $this->nonces_table_name)); | ||
|  |     } | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | ?>
 |