gnu-social/extlib/Auth/OpenID/MDB2Store.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));
}
}
?>