ILIAS  release_4-3 Revision
 All Data Structures Namespaces Files Functions Variables Groups Pages
SQLStore.php
Go to the documentation of this file.
1 <?php
2 
19 require_once 'Auth/OpenID/Interface.php';
20 require_once 'Auth/OpenID/Nonce.php';
21 
25 require_once 'Auth/OpenID.php';
26 
30 require_once 'Auth/OpenID/Nonce.php';
31 
58 
78  function Auth_OpenID_SQLStore($connection,
79  $associations_table = null,
80  $nonces_table = null)
81  {
82  $this->associations_table_name = "oid_associations";
83  $this->nonces_table_name = "oid_nonces";
84 
85  // Check the connection object type to be sure it's a PEAR
86  // database connection.
87  if (!(is_object($connection) &&
88  (is_subclass_of($connection, 'db_common') ||
89  is_subclass_of($connection,
90  'auth_openid_databaseconnection')))) {
91  trigger_error("Auth_OpenID_SQLStore expected PEAR connection " .
92  "object (got ".get_class($connection).")",
93  E_USER_ERROR);
94  return;
95  }
96 
97  $this->connection = $connection;
98 
99  // Be sure to set the fetch mode so the results are keyed on
100  // column name instead of column index. This is a PEAR
101  // constant, so only try to use it if PEAR is present. Note
102  // that Auth_Openid_Databaseconnection instances need not
103  // implement ::setFetchMode for this reason.
104  if (is_subclass_of($this->connection, 'db_common')) {
105  $this->connection->setFetchMode(DB_FETCHMODE_ASSOC);
106  }
107 
108  if ($associations_table) {
109  $this->associations_table_name = $associations_table;
110  }
111 
112  if ($nonces_table) {
113  $this->nonces_table_name = $nonces_table;
114  }
115 
116  $this->max_nonce_age = 6 * 60 * 60;
117 
118  // Be sure to run the database queries with auto-commit mode
119  // turned OFF, because we want every function to run in a
120  // transaction, implicitly. As a rule, methods named with a
121  // leading underscore will NOT control transaction behavior.
122  // Callers of these methods will worry about transactions.
123  $this->connection->autoCommit(false);
124 
125  // Create an empty SQL strings array.
126  $this->sql = array();
127 
128  // Call this method (which should be overridden by subclasses)
129  // to populate the $this->sql array with SQL strings.
130  $this->setSQL();
131 
132  // Verify that all required SQL statements have been set, and
133  // raise an error if any expected SQL strings were either
134  // absent or empty.
135  list($missing, $empty) = $this->_verifySQL();
136 
137  if ($missing) {
138  trigger_error("Expected keys in SQL query list: " .
139  implode(", ", $missing),
140  E_USER_ERROR);
141  return;
142  }
143 
144  if ($empty) {
145  trigger_error("SQL list keys have no SQL strings: " .
146  implode(", ", $empty),
147  E_USER_ERROR);
148  return;
149  }
150 
151  // Add table names to queries.
152  $this->_fixSQL();
153  }
154 
155  function tableExists($table_name)
156  {
157  return !$this->isError(
158  $this->connection->query(
159  sprintf("SELECT * FROM %s LIMIT 0",
160  $table_name)));
161  }
162 
167  function isError($value)
168  {
169  return PEAR::isError($value);
170  }
171 
177  function resultToBool($obj)
178  {
179  if ($this->isError($obj)) {
180  return false;
181  } else {
182  return true;
183  }
184  }
185 
191  function setSQL()
192  {
193  }
194 
199  function reset()
200  {
201  $this->connection->query(sprintf("DELETE FROM %s",
202  $this->associations_table_name));
203 
204  $this->connection->query(sprintf("DELETE FROM %s",
205  $this->nonces_table_name));
206  }
207 
211  function _verifySQL()
212  {
213  $missing = array();
214  $empty = array();
215 
216  $required_sql_keys = array(
217  'nonce_table',
218  'assoc_table',
219  'set_assoc',
220  'get_assoc',
221  'get_assocs',
222  'remove_assoc'
223  );
224 
225  foreach ($required_sql_keys as $key) {
226  if (!array_key_exists($key, $this->sql)) {
227  $missing[] = $key;
228  } else if (!$this->sql[$key]) {
229  $empty[] = $key;
230  }
231  }
232 
233  return array($missing, $empty);
234  }
235 
239  function _fixSQL()
240  {
241  $replacements = array(
242  array(
243  'value' => $this->nonces_table_name,
244  'keys' => array('nonce_table',
245  'add_nonce',
246  'clean_nonce')
247  ),
248  array(
249  'value' => $this->associations_table_name,
250  'keys' => array('assoc_table',
251  'set_assoc',
252  'get_assoc',
253  'get_assocs',
254  'remove_assoc',
255  'clean_assoc')
256  )
257  );
258 
259  foreach ($replacements as $item) {
260  $value = $item['value'];
261  $keys = $item['keys'];
262 
263  foreach ($keys as $k) {
264  if (is_array($this->sql[$k])) {
265  foreach ($this->sql[$k] as $part_key => $part_value) {
266  $this->sql[$k][$part_key] = sprintf($part_value,
267  $value);
268  }
269  } else {
270  $this->sql[$k] = sprintf($this->sql[$k], $value);
271  }
272  }
273  }
274  }
275 
276  function blobDecode($blob)
277  {
278  return $blob;
279  }
280 
281  function blobEncode($str)
282  {
283  return $str;
284  }
285 
286  function createTables()
287  {
288  $this->connection->autoCommit(true);
289  $n = $this->create_nonce_table();
290  $a = $this->create_assoc_table();
291  $this->connection->autoCommit(false);
292 
293  if ($n && $a) {
294  return true;
295  } else {
296  return false;
297  }
298  }
299 
301  {
302  if (!$this->tableExists($this->nonces_table_name)) {
303  $r = $this->connection->query($this->sql['nonce_table']);
304  return $this->resultToBool($r);
305  }
306  return true;
307  }
308 
310  {
311  if (!$this->tableExists($this->associations_table_name)) {
312  $r = $this->connection->query($this->sql['assoc_table']);
313  return $this->resultToBool($r);
314  }
315  return true;
316  }
317 
321  function _set_assoc($server_url, $handle, $secret, $issued,
322  $lifetime, $assoc_type)
323  {
324  return $this->connection->query($this->sql['set_assoc'],
325  array(
326  $server_url,
327  $handle,
328  $secret,
329  $issued,
330  $lifetime,
331  $assoc_type));
332  }
333 
334  function storeAssociation($server_url, $association)
335  {
336  if ($this->resultToBool($this->_set_assoc(
337  $server_url,
338  $association->handle,
339  $this->blobEncode(
340  $association->secret),
341  $association->issued,
342  $association->lifetime,
343  $association->assoc_type
344  ))) {
345  $this->connection->commit();
346  } else {
347  $this->connection->rollback();
348  }
349  }
350 
354  function _get_assoc($server_url, $handle)
355  {
356  $result = $this->connection->getRow($this->sql['get_assoc'],
357  array($server_url, $handle));
358  if ($this->isError($result)) {
359  return null;
360  } else {
361  return $result;
362  }
363  }
364 
368  function _get_assocs($server_url)
369  {
370  $result = $this->connection->getAll($this->sql['get_assocs'],
371  array($server_url));
372 
373  if ($this->isError($result)) {
374  return array();
375  } else {
376  return $result;
377  }
378  }
379 
380  function removeAssociation($server_url, $handle)
381  {
382  if ($this->_get_assoc($server_url, $handle) == null) {
383  return false;
384  }
385 
386  if ($this->resultToBool($this->connection->query(
387  $this->sql['remove_assoc'],
388  array($server_url, $handle)))) {
389  $this->connection->commit();
390  } else {
391  $this->connection->rollback();
392  }
393 
394  return true;
395  }
396 
397  function getAssociation($server_url, $handle = null)
398  {
399  if ($handle !== null) {
400  $assoc = $this->_get_assoc($server_url, $handle);
401 
402  $assocs = array();
403  if ($assoc) {
404  $assocs[] = $assoc;
405  }
406  } else {
407  $assocs = $this->_get_assocs($server_url);
408  }
409 
410  if (!$assocs || (count($assocs) == 0)) {
411  return null;
412  } else {
413  $associations = array();
414 
415  foreach ($assocs as $assoc_row) {
416  $assoc = new Auth_OpenID_Association($assoc_row['handle'],
417  $assoc_row['secret'],
418  $assoc_row['issued'],
419  $assoc_row['lifetime'],
420  $assoc_row['assoc_type']);
421 
422  $assoc->secret = $this->blobDecode($assoc->secret);
423 
424  if ($assoc->getExpiresIn() == 0) {
425  $this->removeAssociation($server_url, $assoc->handle);
426  } else {
427  $associations[] = array($assoc->issued, $assoc);
428  }
429  }
430 
431  if ($associations) {
432  $issued = array();
433  $assocs = array();
434  foreach ($associations as $key => $assoc) {
435  $issued[$key] = $assoc[0];
436  $assocs[$key] = $assoc[1];
437  }
438 
439  array_multisort($issued, SORT_DESC, $assocs, SORT_DESC,
440  $associations);
441 
442  // return the most recently issued one.
443  list($issued, $assoc) = $associations[0];
444  return $assoc;
445  } else {
446  return null;
447  }
448  }
449  }
450 
454  function _add_nonce($server_url, $timestamp, $salt)
455  {
456  $sql = $this->sql['add_nonce'];
457  $result = $this->connection->query($sql, array($server_url,
458  $timestamp,
459  $salt));
460  if ($this->isError($result)) {
461  $this->connection->rollback();
462  } else {
463  $this->connection->commit();
464  }
465  return $this->resultToBool($result);
466  }
467 
468  function useNonce($server_url, $timestamp, $salt)
469  {
470  global $Auth_OpenID_SKEW;
471 
472  if ( abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
473  return false;
474  }
475 
476  return $this->_add_nonce($server_url, $timestamp, $salt);
477  }
478 
486  function _octify($str)
487  {
488  $result = "";
489  for ($i = 0; $i < Auth_OpenID::bytes($str); $i++) {
490  $ch = substr($str, $i, 1);
491  if ($ch == "\\") {
492  $result .= "\\\\\\\\";
493  } else if (ord($ch) == 0) {
494  $result .= "\\\\000";
495  } else {
496  $result .= "\\" . strval(decoct(ord($ch)));
497  }
498  }
499  return $result;
500  }
501 
508  function _unoctify($str)
509  {
510  $result = "";
511  $i = 0;
512  while ($i < strlen($str)) {
513  $char = $str[$i];
514  if ($char == "\\") {
515  // Look to see if the next char is a backslash and
516  // append it.
517  if ($str[$i + 1] != "\\") {
518  $octal_digits = substr($str, $i + 1, 3);
519  $dec = octdec($octal_digits);
520  $char = chr($dec);
521  $i += 4;
522  } else {
523  $char = "\\";
524  $i += 2;
525  }
526  } else {
527  $i += 1;
528  }
529 
530  $result .= $char;
531  }
532 
533  return $result;
534  }
535 
536  function cleanupNonces()
537  {
538  global $Auth_OpenID_SKEW;
539  $v = time() - $Auth_OpenID_SKEW;
540 
541  $this->connection->query($this->sql['clean_nonce'], array($v));
542  $num = $this->connection->affectedRows();
543  $this->connection->commit();
544  return $num;
545  }
546 
548  {
549  $this->connection->query($this->sql['clean_assoc'],
550  array(time()));
551  $num = $this->connection->affectedRows();
552  $this->connection->commit();
553  return $num;
554  }
555 }
556 
557