]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/Auth/OpenID/MDB2Store.php
Merge remote-tracking branch 'upstream/master'
[quix0rs-gnu-social.git] / extlib / Auth / OpenID / MDB2Store.php
1 <?php
2
3 /**
4  * SQL-backed OpenID stores for use with PEAR::MDB2.
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. <openid@janrain.com>
12  * @copyright 2005 Janrain, Inc.
13  * @license http://www.gnu.org/copyleft/lesser.html LGPL
14  */
15
16 require_once 'MDB2.php';
17
18 /**
19  * @access private
20  */
21 require_once 'Auth/OpenID/Interface.php';
22
23 /**
24  * @access private
25  */
26 require_once 'Auth/OpenID.php';
27
28 /**
29  * @access private
30  */
31 require_once 'Auth/OpenID/Nonce.php';
32
33 /**
34  * This store uses a PEAR::MDB2 connection to store persistence
35  * information.
36  *
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.
40  *
41  * To create the tables with the proper schema, see the createTables
42  * method.
43  *
44  * @package OpenID
45  */
46 class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
47     /**
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.
51      *
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
55      * handle.
56      *
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'.
60      *
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'.
64      */
65     function Auth_OpenID_MDB2Store($connection,
66                                   $associations_table = null,
67                                   $nonces_table = null)
68     {
69         $this->associations_table_name = "oid_associations";
70         $this->nonces_table_name = "oid_nonces";
71
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).")",
78                           E_USER_ERROR);
79             return;
80         }
81
82         $this->connection = $connection;
83
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);
87         
88         if (@PEAR::isError($this->connection->loadModule('Extended'))) {
89             trigger_error("Unable to load MDB2_Extended module", E_USER_ERROR);
90             return;
91         }
92
93         if ($associations_table) {
94             $this->associations_table_name = $associations_table;
95         }
96
97         if ($nonces_table) {
98             $this->nonces_table_name = $nonces_table;
99         }
100
101         $this->max_nonce_age = 6 * 60 * 60;
102     }
103
104     function tableExists($table_name)
105     {
106         return !@PEAR::isError($this->connection->query(
107                                   sprintf("SELECT * FROM %s LIMIT 0",
108                                           $table_name)));
109     }
110
111     function createTables()
112     {
113         $n = $this->create_nonce_table();
114         $a = $this->create_assoc_table();
115
116         if (!$n || !$a) {
117             return false;
118         }
119         return true;
120     }
121
122     function create_nonce_table()
123     {
124         if (!$this->tableExists($this->nonces_table_name)) {
125             switch ($this->connection->phptype) {
126                 case "mysql":
127                 case "mysqli":
128                     // Custom SQL for MySQL to use InnoDB and variable-
129                     // length keys
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".
136                                 ") TYPE=InnoDB",
137                                 $this->nonces_table_name));
138                     if (@PEAR::isError($r)) {
139                         return false;
140                     }
141                     break;
142                 default:
143                     if (@PEAR::isError(
144                         $this->connection->loadModule('Manager'))) {
145                         return false;
146                     }
147                     $fields = array(
148                         "server_url" => array(
149                             "type" => "text",
150                             "length" => 2047,
151                             "notnull" => true
152                         ),
153                         "timestamp" => array(
154                             "type" => "integer",
155                             "notnull" => true
156                         ),
157                         "salt" => array(
158                             "type" => "text",
159                             "length" => 40,
160                             "fixed" => true,
161                             "notnull" => true
162                         )
163                     );
164                     $constraint = array(
165                         "unique" => 1,
166                         "fields" => array(
167                             "server_url" => true,
168                             "timestamp" => true,
169                             "salt" => true
170                         )
171                     );
172                     
173                     $r = $this->connection->createTable($this->nonces_table_name,
174                                                         $fields);
175                     if (@PEAR::isError($r)) {
176                         return false;
177                     }
178                     
179                     $r = $this->connection->createConstraint(
180                         $this->nonces_table_name,
181                         $this->nonces_table_name . "_constraint",
182                         $constraint);
183                     if (@PEAR::isError($r)) {
184                         return false;
185                     }
186                     break;
187             }
188         }
189         return true;
190     }
191
192     function create_assoc_table()
193     {
194         if (!$this->tableExists($this->associations_table_name)) {
195             switch ($this->connection->phptype) {
196                 case "mysql":
197                 case "mysqli":
198                     // Custom SQL for MySQL to use InnoDB and variable-
199                     // length keys
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".
209                                 ") TYPE=InnoDB",
210                             $this->associations_table_name));
211                     if (@PEAR::isError($r)) {
212                         return false;
213                     }
214                     break;
215                 default:
216                     if (@PEAR::isError(
217                         $this->connection->loadModule('Manager'))) {
218                         return false;
219                     }
220                     $fields = array(
221                         "server_url" => array(
222                             "type" => "text",
223                             "length" => 2047,
224                             "notnull" => true
225                         ),
226                         "handle" => array(
227                             "type" => "text",
228                             "length" => 255,
229                             "notnull" => true
230                         ),
231                         "secret" => array(
232                             "type" => "blob",
233                             "length" => "255",
234                             "notnull" => true
235                         ),
236                         "issued" => array(
237                             "type" => "integer",
238                             "notnull" => true
239                         ),
240                         "lifetime" => array(
241                             "type" => "integer",
242                             "notnull" => true
243                         ),
244                         "assoc_type" => array(
245                             "type" => "text",
246                             "length" => 64,
247                             "notnull" => true
248                         )
249                     );
250                     $options = array(
251                         "primary" => array(
252                             "server_url" => true,
253                             "handle" => true
254                         )
255                     );
256                     
257                     $r = $this->connection->createTable(
258                         $this->associations_table_name,
259                         $fields,
260                         $options);
261                     if (@PEAR::isError($r)) {
262                         return false;
263                     }
264                     break;
265             }
266         }
267         return true;
268     }
269
270     function storeAssociation($server_url, $association)
271     {
272         $fields = array(
273             "server_url" => array(
274                 "value" => $server_url,
275                 "key" => true
276             ),
277             "handle" => array(
278                 "value" => $association->handle,
279                 "key" => true
280             ),
281             "secret" => array(
282                 "value" => $association->secret,
283                 "type" => "blob"
284             ),
285             "issued" => array(
286                 "value" => $association->issued
287             ),
288             "lifetime" => array(
289                 "value" => $association->lifetime
290             ),
291             "assoc_type" => array(
292                 "value" => $association->assoc_type
293             )
294         );
295         
296         return !@PEAR::isError($this->connection->replace(
297                                   $this->associations_table_name,
298                                   $fields));
299     }
300
301     function cleanupNonces()
302     {
303         global $Auth_OpenID_SKEW;
304         $v = time() - $Auth_OpenID_SKEW;
305
306         return $this->connection->exec(
307             sprintf("DELETE FROM %s WHERE timestamp < %d",
308                     $this->nonces_table_name, $v));
309     }
310
311     function cleanupAssociations()
312     {
313         return $this->connection->exec(
314             sprintf("DELETE FROM %s WHERE issued + lifetime < %d",
315                     $this->associations_table_name, time()));
316     }
317
318     function getAssociation($server_url, $handle = null)
319     {
320         $sql = "";
321         $params = null;
322         $types = array(
323                        "text",
324                        "blob",
325                        "integer",
326                        "integer",
327                        "text"
328                        );
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);
334         } else {
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);
339         }
340         
341         $assoc = $this->connection->getRow($sql, $types, $params);
342
343         if (!$assoc || @PEAR::isError($assoc)) {
344             return null;
345         } else {
346             $association = new Auth_OpenID_Association($assoc['handle'],
347                                                        stream_get_contents(
348                                                            $assoc['secret']),
349                                                        $assoc['issued'],
350                                                        $assoc['lifetime'],
351                                                        $assoc['assoc_type']);
352             fclose($assoc['secret']);
353             return $association;
354         }
355     }
356
357     function removeAssociation($server_url, $handle)
358     {
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));
363         
364         if (@PEAR::isError($r) || $r == 0) {
365             return false;
366         }
367         return true;
368     }
369
370     function useNonce($server_url, $timestamp, $salt)
371     {
372         global $Auth_OpenID_SKEW;
373
374         if (abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
375             return false;
376         }
377         
378         $fields = array(
379                         "timestamp" => $timestamp,
380                         "salt" => $salt
381                         );
382         
383         if (!empty($server_url)) {
384             $fields["server_url"] = $server_url;
385         }
386         
387         $r = $this->connection->autoExecute(
388             $this->nonces_table_name,
389             $fields,
390             MDB2_AUTOQUERY_INSERT);
391         
392         if (@PEAR::isError($r)) {
393             return false;
394         }
395         return true;
396     }
397
398     /**
399      * Resets the store by removing all records from the store's
400      * tables.
401      */
402     function reset()
403     {
404         $this->connection->query(sprintf("DELETE FROM %s",
405                                          $this->associations_table_name));
406
407         $this->connection->query(sprintf("DELETE FROM %s",
408                                          $this->nonces_table_name));
409     }
410
411 }
412
413 ?>