1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15:
16: require_once 'MDB2.php';
17:
18: 19: 20:
21: require_once 'Auth/OpenID/Interface.php';
22:
23: 24: 25:
26: require_once 'Auth/OpenID.php';
27:
28: 29: 30:
31: require_once 'Auth/OpenID/Nonce.php';
32:
33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45:
46: class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 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:
73:
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:
85:
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:
129:
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:
199:
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: 400: 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: ?>
414: