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 38 /** 39 * Initializes a new {@link Auth_OpenID_MemcachedStore} instance. 40 * Just saves memcached object as property. 41 * 42 * @param resource connection Memcache connection resourse 43 */ 44 function Auth_OpenID_MemcachedStore($connection, $compress = false) 45 { 46 $this->connection = $connection; 47 $this->compress = $compress ? MEMCACHE_COMPRESSED : 0; 48 } 49 50 /** 51 * Store association until its expiration time in memcached. 52 * Overwrites any existing association with same server_url and 53 * handle. Handles list of associations for every server. 54 */ 55 function storeAssociation($server_url, $association) 56 { 57 // create memcached keys for association itself 58 // and list of associations for this server 59 $associationKey = $this->associationKey($server_url, 60 $association->handle); 61 $serverKey = $this->associationServerKey($server_url); 62 63 // get list of associations 64 $serverAssociations = $this->connection->get($serverKey); 65 66 // if no such list, initialize it with empty array 67 if (!$serverAssociations) { 68 $serverAssociations = array(); 69 } 70 // and store given association key in it 71 $serverAssociations[$association->issued] = $associationKey; 72 73 // save associations' keys list 74 $this->connection->set( 75 $serverKey, 76 $serverAssociations, 77 $this->compress 78 ); 79 // save association itself 80 $this->connection->set( 81 $associationKey, 82 $association, 83 $this->compress, 84 $association->issued + $association->lifetime); 85 } 86 87 /** 88 * Read association from memcached. If no handle given 89 * and multiple associations found, returns latest issued 90 */ 91 function getAssociation($server_url, $handle = null) 92 { 93 // simple case: handle given 94 if ($handle !== null) { 95 // get association, return null if failed 96 $association = $this->connection->get( 97 $this->associationKey($server_url, $handle)); 98 return $association ? $association : null; 99 } 100 101 // no handle given, working with list 102 // create key for list of associations 103 $serverKey = $this->associationServerKey($server_url); 104 105 // get list of associations 106 $serverAssociations = $this->connection->get($serverKey); 107 // return null if failed or got empty list 108 if (!$serverAssociations) { 109 return null; 110 } 111 112 // get key of most recently issued association 113 $keys = array_keys($serverAssociations); 114 sort($keys); 115 $lastKey = $serverAssociations[array_pop($keys)]; 116 117 // get association, return null if failed 118 $association = $this->connection->get($lastKey); 119 return $association ? $association : null; 120 } 121 122 /** 123 * Immediately delete association from memcache. 124 */ 125 function removeAssociation($server_url, $handle) 126 { 127 // create memcached keys for association itself 128 // and list of associations for this server 129 $serverKey = $this->associationServerKey($server_url); 130 $associationKey = $this->associationKey($server_url, 131 $handle); 132 133 // get list of associations 134 $serverAssociations = $this->connection->get($serverKey); 135 // return null if failed or got empty list 136 if (!$serverAssociations) { 137 return false; 138 } 139 140 // ensure that given association key exists in list 141 $serverAssociations = array_flip($serverAssociations); 142 if (!array_key_exists($associationKey, $serverAssociations)) { 143 return false; 144 } 145 146 // remove given association key from list 147 unset($serverAssociations[$associationKey]); 148 $serverAssociations = array_flip($serverAssociations); 149 150 // save updated list 151 $this->connection->set( 152 $serverKey, 153 $serverAssociations, 154 $this->compress 155 ); 156 157 // delete association 158 return $this->connection->delete($associationKey); 159 } 160 161 /** 162 * Create nonce for server and salt, expiring after 163 * $Auth_OpenID_SKEW seconds. 164 */ 165 function useNonce($server_url, $timestamp, $salt) 166 { 167 global $Auth_OpenID_SKEW; 168 169 // save one request to memcache when nonce obviously expired 170 if (abs($timestamp - time()) > $Auth_OpenID_SKEW) { 171 return false; 172 } 173 174 // returns false when nonce already exists 175 // otherwise adds nonce 176 return $this->connection->add( 177 'openid_nonce_' . sha1($server_url) . '_' . sha1($salt), 178 1, // any value here 179 $this->compress, 180 $Auth_OpenID_SKEW); 181 } 182 183 /** 184 * Memcache key is prefixed with 'openid_association_' string. 185 */ 186 function associationKey($server_url, $handle = null) 187 { 188 return 'openid_association_' . sha1($server_url) . '_' . sha1($handle); 189 } 190 191 /** 192 * Memcache key is prefixed with 'openid_association_' string. 193 */ 194 function associationServerKey($server_url) 195 { 196 return 'openid_association_server_' . sha1($server_url); 197 } 198 199 /** 200 * Report that this storage doesn't support cleanup 201 */ 202 function supportsCleanup() 203 { 204 return false; 205 } 206} 207 208