MemcachedStore.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <?php
  2. /**
  3. * This file supplies a memcached store backend for OpenID servers and
  4. * consumers.
  5. *
  6. * PHP versions 4 and 5
  7. *
  8. * LICENSE: See the COPYING file included in this distribution.
  9. *
  10. * @package OpenID
  11. * @author Artemy Tregubenko <me@arty.name>
  12. * @copyright 2008 JanRain, Inc.
  13. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
  14. * Contributed by Open Web Technologies <http://openwebtech.ru/>
  15. */
  16. /**
  17. * Import the interface for creating a new store class.
  18. */
  19. require_once 'Auth/OpenID/Interface.php';
  20. /**
  21. * This is a memcached-based store for OpenID associations and
  22. * nonces.
  23. *
  24. * As memcache has limit of 250 chars for key length,
  25. * server_url, handle and salt are hashed with sha1().
  26. *
  27. * Most of the methods of this class are implementation details.
  28. * People wishing to just use this store need only pay attention to
  29. * the constructor.
  30. *
  31. * @package OpenID
  32. */
  33. class Auth_OpenID_MemcachedStore extends Auth_OpenID_OpenIDStore {
  34. /**
  35. * Initializes a new {@link Auth_OpenID_MemcachedStore} instance.
  36. * Just saves memcached object as property.
  37. *
  38. * @param resource connection Memcache connection resourse
  39. */
  40. function Auth_OpenID_MemcachedStore($connection, $compress = false)
  41. {
  42. $this->connection = $connection;
  43. $this->compress = $compress ? MEMCACHE_COMPRESSED : 0;
  44. }
  45. /**
  46. * Store association until its expiration time in memcached.
  47. * Overwrites any existing association with same server_url and
  48. * handle. Handles list of associations for every server.
  49. */
  50. function storeAssociation($server_url, $association)
  51. {
  52. // create memcached keys for association itself
  53. // and list of associations for this server
  54. $associationKey = $this->associationKey($server_url,
  55. $association->handle);
  56. $serverKey = $this->associationServerKey($server_url);
  57. // get list of associations
  58. $serverAssociations = $this->connection->get($serverKey);
  59. // if no such list, initialize it with empty array
  60. if (!$serverAssociations) {
  61. $serverAssociations = array();
  62. }
  63. // and store given association key in it
  64. $serverAssociations[$association->issued] = $associationKey;
  65. // save associations' keys list
  66. $this->connection->set(
  67. $serverKey,
  68. $serverAssociations,
  69. $this->compress
  70. );
  71. // save association itself
  72. $this->connection->set(
  73. $associationKey,
  74. $association,
  75. $this->compress,
  76. $association->issued + $association->lifetime);
  77. }
  78. /**
  79. * Read association from memcached. If no handle given
  80. * and multiple associations found, returns latest issued
  81. */
  82. function getAssociation($server_url, $handle = null)
  83. {
  84. // simple case: handle given
  85. if ($handle !== null) {
  86. // get association, return null if failed
  87. $association = $this->connection->get(
  88. $this->associationKey($server_url, $handle));
  89. return $association ? $association : null;
  90. }
  91. // no handle given, working with list
  92. // create key for list of associations
  93. $serverKey = $this->associationServerKey($server_url);
  94. // get list of associations
  95. $serverAssociations = $this->connection->get($serverKey);
  96. // return null if failed or got empty list
  97. if (!$serverAssociations) {
  98. return null;
  99. }
  100. // get key of most recently issued association
  101. $keys = array_keys($serverAssociations);
  102. sort($keys);
  103. $lastKey = $serverAssociations[array_pop($keys)];
  104. // get association, return null if failed
  105. $association = $this->connection->get($lastKey);
  106. return $association ? $association : null;
  107. }
  108. /**
  109. * Immediately delete association from memcache.
  110. */
  111. function removeAssociation($server_url, $handle)
  112. {
  113. // create memcached keys for association itself
  114. // and list of associations for this server
  115. $serverKey = $this->associationServerKey($server_url);
  116. $associationKey = $this->associationKey($server_url,
  117. $handle);
  118. // get list of associations
  119. $serverAssociations = $this->connection->get($serverKey);
  120. // return null if failed or got empty list
  121. if (!$serverAssociations) {
  122. return false;
  123. }
  124. // ensure that given association key exists in list
  125. $serverAssociations = array_flip($serverAssociations);
  126. if (!array_key_exists($associationKey, $serverAssociations)) {
  127. return false;
  128. }
  129. // remove given association key from list
  130. unset($serverAssociations[$associationKey]);
  131. $serverAssociations = array_flip($serverAssociations);
  132. // save updated list
  133. $this->connection->set(
  134. $serverKey,
  135. $serverAssociations,
  136. $this->compress
  137. );
  138. // delete association
  139. return $this->connection->delete($associationKey);
  140. }
  141. /**
  142. * Create nonce for server and salt, expiring after
  143. * $Auth_OpenID_SKEW seconds.
  144. */
  145. function useNonce($server_url, $timestamp, $salt)
  146. {
  147. global $Auth_OpenID_SKEW;
  148. // save one request to memcache when nonce obviously expired
  149. if (abs($timestamp - time()) > $Auth_OpenID_SKEW) {
  150. return false;
  151. }
  152. // returns false when nonce already exists
  153. // otherwise adds nonce
  154. return $this->connection->add(
  155. 'openid_nonce_' . sha1($server_url) . '_' . sha1($salt),
  156. 1, // any value here
  157. $this->compress,
  158. $Auth_OpenID_SKEW);
  159. }
  160. /**
  161. * Memcache key is prefixed with 'openid_association_' string.
  162. */
  163. function associationKey($server_url, $handle = null)
  164. {
  165. return 'openid_association_' . sha1($server_url) . '_' . sha1($handle);
  166. }
  167. /**
  168. * Memcache key is prefixed with 'openid_association_' string.
  169. */
  170. function associationServerKey($server_url)
  171. {
  172. return 'openid_association_server_' . sha1($server_url);
  173. }
  174. /**
  175. * Report that this storage doesn't support cleanup
  176. */
  177. function supportsCleanup()
  178. {
  179. return false;
  180. }
  181. }