4 * SQL-backed OpenID stores for use with PEAR::MDB2.
8 * LICENSE: See the COPYING file included in this distribution.
11 * @author JanRain, Inc. <openid@janrain.com>
12 * @copyright 2005 Janrain, Inc.
13 * @license http://www.gnu.org/copyleft/lesser.html LGPL
16 require_once 'MDB2.php';
21 require_once 'Auth/OpenID/Interface.php';
26 require_once 'Auth/OpenID.php';
31 require_once 'Auth/OpenID/Nonce.php';
34 * This store uses a PEAR::MDB2 connection to store persistence
37 * The table names used are determined by the class variables
38 * associations_table_name and nonces_table_name. To change the name
39 * of the tables used, pass new table names into the constructor.
41 * To create the tables with the proper schema, see the createTables
46 class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
48 * This creates a new MDB2Store instance. It requires an
49 * established database connection be given to it, and it allows
50 * overriding the default table names.
52 * @param connection $connection This must be an established
53 * connection to a database of the correct type for the SQLStore
54 * subclass you're using. This must be a PEAR::MDB2 connection
57 * @param associations_table: This is an optional parameter to
58 * specify the name of the table used for storing associations.
59 * The default value is 'oid_associations'.
61 * @param nonces_table: This is an optional parameter to specify
62 * the name of the table used for storing nonces. The default
63 * value is 'oid_nonces'.
65 function Auth_OpenID_MDB2Store($connection,
66 $associations_table = null,
69 $this->associations_table_name = "oid_associations";
70 $this->nonces_table_name = "oid_nonces";
72 // Check the connection object type to be sure it's a PEAR
73 // database connection.
74 if (!is_object($connection) ||
75 !is_subclass_of($connection, 'mdb2_driver_common')) {
76 trigger_error("Auth_OpenID_MDB2Store expected PEAR connection " .
77 "object (got ".get_class($connection).")",
82 $this->connection = $connection;
84 // Be sure to set the fetch mode so the results are keyed on
85 // column name instead of column index.
86 $this->connection->setFetchMode(MDB2_FETCHMODE_ASSOC);
88 if (PEAR::isError($this->connection->loadModule('Extended'))) {
89 trigger_error("Unable to load MDB2_Extended module", E_USER_ERROR);
93 if ($associations_table) {
94 $this->associations_table_name = $associations_table;
98 $this->nonces_table_name = $nonces_table;
101 $this->max_nonce_age = 6 * 60 * 60;
104 function tableExists($table_name)
106 return !PEAR::isError($this->connection->query(
107 sprintf("SELECT * FROM %s LIMIT 0",
111 function createTables()
113 $n = $this->create_nonce_table();
114 $a = $this->create_assoc_table();
122 function create_nonce_table()
124 if (!$this->tableExists($this->nonces_table_name)) {
125 switch ($this->connection->phptype) {
128 // Custom SQL for MySQL to use InnoDB and variable-
130 $r = $this->connection->exec(
131 sprintf("CREATE TABLE %s (\n".
132 " server_url VARCHAR(2047) NOT NULL DEFAULT '',\n".
133 " timestamp INTEGER NOT NULL,\n".
134 " salt CHAR(40) NOT NULL,\n".
135 " UNIQUE (server_url(255), timestamp, salt)\n".
137 $this->nonces_table_name));
138 if (PEAR::isError($r)) {
144 $this->connection->loadModule('Manager'))) {
148 "server_url" => array(
153 "timestamp" => array(
167 "server_url" => true,
173 $r = $this->connection->createTable($this->nonces_table_name,
175 if (PEAR::isError($r)) {
179 $r = $this->connection->createConstraint(
180 $this->nonces_table_name,
181 $this->nonces_table_name . "_constraint",
183 if (PEAR::isError($r)) {
192 function create_assoc_table()
194 if (!$this->tableExists($this->associations_table_name)) {
195 switch ($this->connection->phptype) {
198 // Custom SQL for MySQL to use InnoDB and variable-
200 $r = $this->connection->exec(
201 sprintf("CREATE TABLE %s(\n".
202 " server_url VARCHAR(2047) NOT NULL DEFAULT '',\n".
203 " handle VARCHAR(255) NOT NULL,\n".
204 " secret BLOB NOT NULL,\n".
205 " issued INTEGER NOT NULL,\n".
206 " lifetime INTEGER NOT NULL,\n".
207 " assoc_type VARCHAR(64) NOT NULL,\n".
208 " PRIMARY KEY (server_url(255), handle)\n".
210 $this->associations_table_name));
211 if (PEAR::isError($r)) {
217 $this->connection->loadModule('Manager'))) {
221 "server_url" => array(
244 "assoc_type" => array(
252 "server_url" => true,
257 $r = $this->connection->createTable(
258 $this->associations_table_name,
261 if (PEAR::isError($r)) {
270 function storeAssociation($server_url, $association)
273 "server_url" => array(
274 "value" => $server_url,
278 "value" => $association->handle,
282 "value" => $association->secret,
286 "value" => $association->issued
289 "value" => $association->lifetime
291 "assoc_type" => array(
292 "value" => $association->assoc_type
296 return !PEAR::isError($this->connection->replace(
297 $this->associations_table_name,
301 function cleanupNonces()
303 global $Auth_OpenID_SKEW;
304 $v = time() - $Auth_OpenID_SKEW;
306 return $this->connection->exec(
307 sprintf("DELETE FROM %s WHERE timestamp < %d",
308 $this->nonces_table_name, $v));
311 function cleanupAssociations()
313 return $this->connection->exec(
314 sprintf("DELETE FROM %s WHERE issued + lifetime < %d",
315 $this->associations_table_name, time()));
318 function getAssociation($server_url, $handle = null)
329 if ($handle !== null) {
330 $sql = sprintf("SELECT handle, secret, issued, lifetime, assoc_type " .
331 "FROM %s WHERE server_url = ? AND handle = ?",
332 $this->associations_table_name);
333 $params = array($server_url, $handle);
335 $sql = sprintf("SELECT handle, secret, issued, lifetime, assoc_type " .
336 "FROM %s WHERE server_url = ? ORDER BY issued DESC",
337 $this->associations_table_name);
338 $params = array($server_url);
341 $assoc = $this->connection->getRow($sql, $types, $params);
343 if (!$assoc || PEAR::isError($assoc)) {
346 $association = new Auth_OpenID_Association($assoc['handle'],
351 $assoc['assoc_type']);
352 fclose($assoc['secret']);
357 function removeAssociation($server_url, $handle)
359 $r = $this->connection->execParam(
360 sprintf("DELETE FROM %s WHERE server_url = ? AND handle = ?",
361 $this->associations_table_name),
362 array($server_url, $handle));
364 if (PEAR::isError($r) || $r == 0) {
370 function useNonce($server_url, $timestamp, $salt)
372 global $Auth_OpenID_SKEW;
374 if (abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
379 "timestamp" => $timestamp,
383 if (!empty($server_url)) {
384 $fields["server_url"] = $server_url;
387 $r = $this->connection->autoExecute(
388 $this->nonces_table_name,
390 MDB2_AUTOQUERY_INSERT);
392 if (PEAR::isError($r)) {
399 * Resets the store by removing all records from the store's
404 $this->connection->query(sprintf("DELETE FROM %s",
405 $this->associations_table_name));
407 $this->connection->query(sprintf("DELETE FROM %s",
408 $this->nonces_table_name));