Browse files

[project @ Add memcache store implementation and tests]

Ignore-this: a8cde55ae2c28a9ba6f8c0a46436d336
  • Loading branch information...
1 parent f1020ee commit 6bff93ec39892cc7ca69724ea89ea3e0fac1ba25 tailor committed Jun 29, 2009
Showing with 139 additions and 1 deletion.
  1. +1 −1 admin/runtests
  2. +10 −0 admin/runtests.rb
  3. +107 −0 lib/openid/store/memcache.rb
  4. +21 −0 test/test_stores.rb
View
2 admin/runtests
@@ -12,4 +12,4 @@ esac
HERE=$(dirname $(readlink --canonicalize "$0"))
REPOROOT=$(dirname "$HERE")
-RUBYLIB="$REPOROOT/lib" $RUBY "$@" "$REPOROOT/admin/runtests.rb"
+TESTING_MEMCACHE="localhost:11211" RUBYLIB="$REPOROOT/lib" $RUBY "$@" "$REPOROOT/admin/runtests.rb"
View
10 admin/runtests.rb
@@ -7,6 +7,16 @@
require 'test/unit/collector/dir'
require 'test/unit/ui/console/testrunner'
+begin
+ require 'rubygems'
+ require 'memcache'
+rescue LoadError
+else
+ if ENV['TESTING_MEMCACHE']
+ TESTING_MEMCACHE = MemCache.new(ENV['TESTING_MEMCACHE'])
+ end
+end
+
def main
old_verbose = $VERBOSE
$VERBOSE = true
View
107 lib/openid/store/memcache.rb
@@ -0,0 +1,107 @@
+require 'openid/util'
+require 'openid/store/interface'
+require 'openid/store/nonce'
+require 'time'
+
+module OpenID
+ module Store
+ class Memcache < Interface
+ attr_accessor :key_prefix
+
+ def initialize(cache_client, key_prefix='openid-store:')
+ @cache_client = cache_client
+ self.key_prefix = key_prefix
+ end
+
+ # Put a Association object into storage.
+ # When implementing a store, don't assume that there are any limitations
+ # on the character set of the server_url. In particular, expect to see
+ # unescaped non-url-safe characters in the server_url field.
+ def store_association(server_url, association)
+ serialized = serialize(association)
+ [nil, association.handle].each do |handle|
+ key = assoc_key(server_url, handle)
+ @cache_client.set(key, serialized, expiry(association.lifetime))
+ end
+ end
+
+ # Returns a Association object from storage that matches
+ # the server_url. Returns nil if no such association is found or if
+ # the one matching association is expired. (Is allowed to GC expired
+ # associations when found.)
+ def get_association(server_url, handle=nil)
+ serialized = @cache_client.get(assoc_key(server_url, handle))
+ if serialized
+ return deserialize(serialized)
+ else
+ return nil
+ end
+ end
+
+ # If there is a matching association, remove it from the store and
+ # return true, otherwise return false.
+ def remove_association(server_url, handle)
+ deleted = delete(assoc_key(server_url, handle))
+ server_assoc = get_association(server_url)
+ if server_assoc && server_assoc.handle == handle
+ deleted = delete(assoc_key(server_url)) | deleted
+ end
+ return deleted
+ end
+
+ # Return true if the nonce has not been used before, and store it
+ # for a while to make sure someone doesn't try to use the same value
+ # again. Return false if the nonce has already been used or if the
+ # timestamp is not current.
+ # You can use OpenID::Store::Nonce::SKEW for your timestamp window.
+ # server_url: URL of the server from which the nonce originated
+ # timestamp: time the nonce was created in seconds since unix epoch
+ # salt: A random string that makes two nonces issued by a server in
+ # the same second unique
+ def use_nonce(server_url, timestamp, salt)
+ return false if (timestamp - Time.now.to_i).abs > Nonce.skew
+ ts = timestamp.to_s # base 10 seconds since epoch
+ nonce_key = key_prefix + 'N' + server_url + '|' + ts + '|' + salt
+ result = @cache_client.add(nonce_key, '', expiry(Nonce.skew + 5))
+ return !!(result =~ /^STORED/)
+ end
+
+ def assoc_key(server_url, assoc_handle=nil)
+ key = key_prefix + 'A' + server_url
+ if assoc_handle
+ key += '|' + assoc_handle
+ end
+ return key
+ end
+
+ def cleanup_nonces
+ end
+
+ def cleanup
+ end
+
+ def cleanup_associations
+ end
+
+ protected
+
+ def delete(key)
+ result = @cache_client.delete(key)
+ return !!(result =~ /^DELETED/)
+ end
+
+ def serialize(assoc)
+ Marshal.dump(assoc)
+ end
+
+ def deserialize(assoc_str)
+ Marshal.load(assoc_str)
+ end
+
+ # Convert a lifetime in seconds into a memcache expiry value
+ def expiry(t)
+ Time.now.to_i + t
+ end
+ end
+ end
+end
View
21 test/test_stores.rb
@@ -1,6 +1,7 @@
require 'test/unit'
require 'openid/store/interface'
require 'openid/store/filesystem'
+require 'openid/store/memcache'
require 'openid/store/memory'
require 'openid/util'
require 'openid/store/nonce'
@@ -235,6 +236,26 @@ def setup
end
end
+ begin
+ ::TESTING_MEMCACHE
+ rescue NameError
+ else
+ class MemcacheStoreTestCase < Test::Unit::TestCase
+ include StoreTestCase
+ def setup
+ store_uniq = OpenID::CryptUtil.random_string(6, "0123456789")
+ store_namespace = "openid-store-#{store_uniq}:"
+ @store = Memcache.new(::TESTING_MEMCACHE, store_namespace)
+ end
+
+ def test_nonce_cleanup
+ end
+
+ def test_assoc_cleanup
+ end
+ end
+ end
+
class AbstractStoreTestCase < Test::Unit::TestCase
def test_abstract_class
# the abstract made concrete

0 comments on commit 6bff93e

Please sign in to comment.