diff options
author | tailor <dag@janrain.com> | 2008-06-02 20:02:24 +0000 |
---|---|---|
committer | tailor <dag@janrain.com> | 2008-06-02 20:02:24 +0000 |
commit | b5a3e401ec6977ce63650712e3b65c607b14afd7 (patch) | |
tree | bc7ed7a987cca0ccbafc048e3bb99938a820b84c /contrib | |
parent | d6cb619d4c3532b966762ebba6261c26caa1e32e (diff) | |
download | php-openid-b5a3e401ec6977ce63650712e3b65c607b14afd7.zip php-openid-b5a3e401ec6977ce63650712e3b65c607b14afd7.tar.gz php-openid-b5a3e401ec6977ce63650712e3b65c607b14afd7.tar.bz2 |
[project @ Move Memcached store to contrib since it's not owned by JanRain]
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/MemcachedStore.php | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/contrib/MemcachedStore.php b/contrib/MemcachedStore.php new file mode 100644 index 0000000..c75384a --- /dev/null +++ b/contrib/MemcachedStore.php @@ -0,0 +1,246 @@ +<?php + +/** + * This file supplies a memcached store backend for OpenID servers and + * consumers. + * + * PHP versions 4 and 5 + * + * LICENSE: See the COPYING file included in this distribution. + * + * @package OpenID + * @author Artemy Tregubenko <me@arty.name> + * @copyright Open Web Technologies <http://openwebtech.ru/> + * @license http://www.gnu.org/copyleft/lesser.html LGPL + */ + +/** + * Import the interface for creating a new store class. + */ +require_once 'Auth/OpenID/Interface.php'; + +/** + * This is a memcached-based store for OpenID associations and + * nonces. + * + * As memcache has limit of 250 chars for key length, + * server_url, handle and salt are hashed with sha1(). + * + * Most of the methods of this class are implementation details. + * People wishing to just use this store need only pay attention to + * the constructor. + * + * @package OpenID + */ +class Auth_OpenID_MemcachedStore extends Auth_OpenID_OpenIDStore { + + /** + * Initializes a new {@link Auth_OpenID_MemcachedStore} instance. + * Just saves memcached object as property. + * + * @param resource connection Memcache connection resourse + */ + function Auth_OpenID_MemcachedStore($connection, $compress = false) + { + $this->connection = $connection; + $this->compress = $compress ? MEMCACHE_COMPRESSED : 0; + } + + /** + * Store association until its expiration time in memcached. + * Overwrites any existing association with same server_url and + * handle. Handles list of associations for every server. + */ + function storeAssociation($server_url, $association) + { + // create memcached keys for association itself + // and list of associations for this server + $associationKey = $this->associationKey($server_url, + $association->handle); + $serverKey = $this->associationServerKey($server_url); + + // get list of associations + $serverAssociations = $this->connection->get($serverKey); + + // if no such list, initialize it with empty array + if (!$serverAssociations) { + $serverAssociations = array(); + } + // and store given association key in it + $serverAssociations[$association->issued] = $associationKey; + + // save associations' keys list + $this->connection->set( + $serverKey, + $serverAssociations, + $this->compress + ); + // save association itself + $this->connection->set( + $associationKey, + $association, + $this->compress, + $association->issued + $association->lifetime); + } + + /** + * Read association from memcached. If no handle given + * and multiple associations found, returns latest issued + */ + function getAssociation($server_url, $handle = null) + { + // simple case: handle given + if ($handle !== null) { + // get association, return null if failed + $association = $this->connection->get( + $this->associationKey($server_url, $handle)); + return $association ? $association : null; + } + + // no handle given, working with list + // create key for list of associations + $serverKey = $this->associationServerKey($server_url); + + // get list of associations + $serverAssociations = $this->connection->get($serverKey); + // return null if failed or got empty list + if (!$serverAssociations) { + return null; + } + + // get key of most recently issued association + $keys = array_keys($serverAssociations); + sort($keys); + $lastKey = $serverAssociations[array_pop($keys)]; + + // get association, return null if failed + $association = $this->connection->get($lastKey); + return $association ? $association : null; + } + + /** + * Immediately delete association from memcache. + */ + function removeAssociation($server_url, $handle) + { + // create memcached keys for association itself + // and list of associations for this server + $serverKey = $this->associationServerKey($server_url); + $associationKey = $this->associationKey($server_url, + $handle); + + // get list of associations + $serverAssociations = $this->connection->get($serverKey); + // return null if failed or got empty list + if (!$serverAssociations) { + return false; + } + + // ensure that given association key exists in list + $serverAssociations = array_flip($serverAssociations); + if (!array_key_exists($associationKey, $serverAssociations)) { + return false; + } + + // remove given association key from list + unset($serverAssociations[$associationKey]); + $serverAssociations = array_flip($serverAssociations); + + // save updated list + $this->connection->set( + $serverKey, + $serverAssociations, + $this->compress + ); + + // delete association + return $this->connection->delete($associationKey); + } + + /** + * Create nonce for server and salt, expiring after + * $Auth_OpenID_SKEW seconds. + */ + function useNonce($server_url, $timestamp, $salt) + { + global $Auth_OpenID_SKEW; + + // save one request to memcache when nonce obviously expired + if (abs($timestamp - time()) > $Auth_OpenID_SKEW) { + return false; + } + + // returns false when nonce already exists + // otherwise adds nonce + return $this->connection->add( + 'openid_nonce_' . sha1($server_url) . '_' . sha1($salt), + 1, // any value here + $this->compress, + $Auth_OpenID_SKEW); + } + + /** + * Memcache key is prefixed with 'openid_association_' string. + */ + function associationKey($server_url, $handle = null) + { + return 'openid_association_' . sha1($server_url) . '_' . sha1($handle); + } + + /** + * Memcache key is prefixed with 'openid_association_' string. + */ + function associationServerKey($server_url) + { + return 'openid_association_server_' . sha1($server_url); + } + + /** + * Report that this storage doesn't support cleanup + */ + function supportsCleanup() + { + return false; + } +} + +if (@require_once('Tests/Auth/OpenID/StoreTest.php')) { + /** + * This is the host that the store test will use + */ + global $_Auth_OpenID_memcache_test_host; + $_Auth_OpenID_memcache_test_host = 'localhost'; + + class Tests_Auth_OpenID_MemcachedStore_Test extends Tests_Auth_OpenID_StoreTest { + function test_memcache() + { + // If the memcache extension isn't loaded or loadable, succeed + // because we can't run the test. + if (!(extension_loaded('memcache') || + @dl('memcache.so') || + @dl('php_memcache.dll'))) { + print "(skipping memcache store tests)"; + $this->pass(); + return; + } + + global $_Auth_OpenID_memcache_test_host; + + $memcached = new Memcache(); + if (!$memcached->connect($_Auth_OpenID_memcache_test_host)) { + print "(skipping memcache store tests - couldn't connect)"; + $this->pass(); + } else { + $store = new Auth_OpenID_MemcachedStore($memcached); + + $this->_testStore($store); + $this->_testNonce($store); + $this->_testNonceCleanup($store); + + $memcached->close(); + } + } + } +} + +?>
\ No newline at end of file |