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