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));
|
|
}
|
|
|
|
}
|
|
|
|
?>
|