openid-store-sequel 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ *.swp
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ before_script:
2
+ - psql -c 'create database openid_store_sequel_test;' -U postgres
3
+
4
+ env: DATABASE_URL=postgres://postgres:@localhost/openid_store_sequel_test
5
+
6
+ language: ruby
7
+
8
+ rvm:
9
+ - 1.9.2
10
+ - 1.9.3
11
+ - jruby-19mode
12
+ - rbx-19mode
13
+
14
+ script: bundle exec rake test --trace
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
data/Makefile ADDED
@@ -0,0 +1,26 @@
1
+ VERSION=$(shell ruby -r./lib/openid/store/sequel/version -e "puts OpenID::Store::Sequel::VERSION")
2
+
3
+ # default the projcet name to the directory name
4
+ PROJECT?=$(notdir $(PWD))
5
+ GEM=$(PROJECT)-$(VERSION).gem
6
+
7
+ .PHONY: test
8
+ test:
9
+ bundle exec rake test
10
+
11
+ .PHONY: package
12
+ package: $(GEM)
13
+
14
+ # Always build the gem
15
+ .PHONY: $(GEM)
16
+ $(GEM):
17
+ gem build $(PROJECT).gemspec
18
+
19
+ .PHONY: install
20
+ install: $(GEM)
21
+ gem install $<
22
+
23
+ # Publish to gemgate
24
+ .PHONY: publish
25
+ publish: $(GEM)
26
+ gem push $(GEM)
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # OpenID Store Sequel
2
+
3
+ ![](https://github.com/dylanegan/openid-store-sequel/raw/master/cat-attack.gif)
4
+
5
+ Storing your OpenIDs in your Sequels.
6
+
7
+ ## Usage
8
+
9
+ ```ruby
10
+ require 'openid/store/sequel'
11
+
12
+ DB = Sequel.connect(ENV['DATABASE_URL'] || "postgres://localhost/openid_store_sequel")
13
+
14
+ DB.create_table! :open_id_associations do
15
+ primary_key :id
16
+ File :server_url, :null => false
17
+ String :handle, :null => false
18
+ File :secret, :null => false
19
+ Integer :issued, :null => false
20
+ Integer :lifetime, :null => false
21
+ String :assoc_type, :null => false
22
+ end
23
+
24
+ DB.create_table! :open_id_nonces do
25
+ primary_key :id
26
+ String :server_url, :null => false
27
+ DateTime :timestamp, :null => false
28
+ String :salt, :null => false
29
+ end
30
+
31
+ server = OpenID::Server::Server.new(OpenID::Store::Sequel.new, ...)
32
+ ```
33
+
34
+ ## License (MIT)
35
+
36
+ Copyright © 2012 [Merman](http://dylanegan.com/)
37
+
38
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
39
+
40
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs.push "lib"
8
+ t.libs.push "test"
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ t.verbose = true
11
+ end
data/cat-attack.gif ADDED
Binary file
@@ -0,0 +1,11 @@
1
+ module OpenID
2
+ module Store
3
+ class Sequel
4
+ class Association < ::Sequel::Model(:open_id_associations)
5
+ def from_record
6
+ OpenID::Association.new(handle, Base64.decode64(secret), Time.at(issued), lifetime, assoc_type)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ module OpenID
2
+ module Store
3
+ class Sequel
4
+ class Nonce < ::Sequel::Model(:open_id_nonces)
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module OpenID
2
+ module Store
3
+ class Sequel
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,77 @@
1
+ require "base64"
2
+ require "sequel"
3
+
4
+ require 'openid/util'
5
+ require 'openid/store/nonce'
6
+ require 'openid/association'
7
+
8
+ module OpenID
9
+ module Store
10
+ class Sequel
11
+ autoload :Association, "openid/store/sequel/association"
12
+ autoload :Nonce, "openid/store/sequel/nonce"
13
+
14
+ def store_association(server_url, assoc)
15
+ remove_association(server_url, assoc.handle)
16
+ OpenID::Store::Sequel::Association.create(
17
+ server_url: server_url,
18
+ handle: assoc.handle,
19
+ secret: Base64.encode64(assoc.secret),
20
+ issued: assoc.issued,
21
+ lifetime: assoc.lifetime,
22
+ assoc_type: assoc.assoc_type
23
+ )
24
+ end
25
+
26
+ def get_association(server_url, handle=nil)
27
+ assocs = if handle.nil? || handle.empty?
28
+ OpenID::Store::Sequel::Association.where(server_url: server_url)
29
+ else
30
+ OpenID::Store::Sequel::Association.where(server_url: server_url, handle: handle)
31
+ end
32
+
33
+ assocs.to_a.reverse.each do |assoc|
34
+ a = assoc.from_record
35
+ if a.expires_in == 0
36
+ assoc.destroy
37
+ else
38
+ return a
39
+ end
40
+ end if assocs.any?
41
+
42
+ return nil
43
+ end
44
+
45
+ def remove_association(server_url, handle)
46
+ OpenID::Store::Sequel::Association.dataset.filter(server_url: server_url, handle: handle).delete > 0 ? true : false
47
+ end
48
+
49
+ def use_nonce(server_url, timestamp, salt)
50
+ return false if OpenID::Store::Sequel::Nonce.first(server_url: server_url, timestamp: Time.at(timestamp), salt: salt) || (timestamp - Time.now.to_i).abs > OpenID::Nonce.skew
51
+ OpenID::Store::Sequel::Nonce.create(server_url: server_url, timestamp: Time.at(timestamp), salt: salt)
52
+ return true
53
+ end
54
+
55
+ def cleanup_nonces
56
+ now = Time.now.to_i
57
+ count = 0
58
+ OpenID::Store::Sequel::Nonce.where("timestamp > '#{Time.at(now + OpenID::Nonce.skew)}' OR timestamp < '#{Time.at(now - OpenID::Nonce.skew)}'").each do |nonce|
59
+ nonce.destroy
60
+ count += 1
61
+ end
62
+ count
63
+ end
64
+
65
+ def cleanup_associations
66
+ count = 0
67
+ OpenID::Store::Sequel::Association.where('issued > 0').each do |association|
68
+ if association.lifetime + association.issued > Time.now.to_i
69
+ association.destroy
70
+ count += 1
71
+ end
72
+ end
73
+ count
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/openid/store/sequel/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Dylan Egan"]
6
+ gem.email = ["dylanegan@gmail.com"]
7
+ gem.description = %q{Storing your OpenIDs in your Sequels.}
8
+ gem.summary = %q{Storing OpenIDs in Sequels.}
9
+ gem.homepage = ""
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "openid-store-sequel"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = OpenID::Store::Sequel::VERSION
17
+
18
+ gem.add_runtime_dependency "ruby-openid"
19
+ gem.add_runtime_dependency "sequel"
20
+
21
+ gem.add_development_dependency "pg"
22
+ gem.add_development_dependency "rake"
23
+ gem.add_development_dependency "sqlite3"
24
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'test/unit'
2
+ require 'openid/store/sequel'
3
+
4
+ DB = Sequel.connect(ENV['DATABASE_URL'] || "postgres://localhost/openid_store_sequel_test")
5
+ DB.create_table! :open_id_associations do
6
+ primary_key :id
7
+ File :server_url, :null => false
8
+ String :handle, :null => false
9
+ File :secret, :null => false
10
+ Integer :issued, :null => false
11
+ Integer :lifetime, :null => false
12
+ String :assoc_type, :null => false
13
+ end
14
+
15
+ DB.create_table! :open_id_nonces do
16
+ primary_key :id
17
+ String :server_url, :null => false
18
+ DateTime :timestamp, :null => false
19
+ String :salt, :null => false
20
+ end
@@ -0,0 +1,219 @@
1
+ require "helper"
2
+
3
+ module OpenID
4
+ module Store
5
+ module StoreTestCase
6
+ @@allowed_handle = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
7
+ @@allowed_nonce = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
8
+
9
+ def _gen_nonce
10
+ OpenID::CryptUtil.random_string(8, @@allowed_nonce)
11
+ end
12
+
13
+ def _gen_handle(n)
14
+ OpenID::CryptUtil.random_string(n, @@allowed_handle)
15
+ end
16
+
17
+ def _gen_secret(n, chars=nil)
18
+ OpenID::CryptUtil.random_string(n, chars)
19
+ end
20
+
21
+ def _gen_assoc(issued, lifetime=600)
22
+ secret = _gen_secret(20)
23
+ handle = _gen_handle(128)
24
+ OpenID::Association.new(handle, secret, Time.now + issued, lifetime,
25
+ 'HMAC-SHA1')
26
+ end
27
+
28
+ def _check_retrieve(url, handle=nil, expected=nil)
29
+ ret_assoc = @store.get_association(url, handle)
30
+
31
+ if expected.nil?
32
+ assert_nil(ret_assoc)
33
+ else
34
+ assert_equal(expected, ret_assoc)
35
+ assert_equal(expected.handle, ret_assoc.handle)
36
+ assert_equal(expected.secret, ret_assoc.secret)
37
+ end
38
+ end
39
+
40
+ def _check_remove(url, handle, expected)
41
+ present = @store.remove_association(url, handle)
42
+ assert_equal(expected, present)
43
+ end
44
+
45
+ def test_store
46
+ assoc = _gen_assoc(issued=0)
47
+
48
+ # Make sure that a missing association returns no result
49
+ _check_retrieve(server_url)
50
+
51
+ # Check that after storage, getting returns the same result
52
+ @store.store_association(server_url, assoc)
53
+ _check_retrieve(server_url, nil, assoc)
54
+
55
+ # more than once
56
+ _check_retrieve(server_url, nil, assoc)
57
+
58
+ # Storing more than once has no ill effect
59
+ @store.store_association(server_url, assoc)
60
+ _check_retrieve(server_url, nil, assoc)
61
+
62
+ # Removing an association that does not exist returns not present
63
+ _check_remove(server_url, assoc.handle + 'x', false)
64
+
65
+ # Removing an association that does not exist returns not present
66
+ _check_remove(server_url + 'x', assoc.handle, false)
67
+
68
+ # Removing an association that is present returns present
69
+ _check_remove(server_url, assoc.handle, true)
70
+
71
+ # but not present on subsequent calls
72
+ _check_remove(server_url, assoc.handle, false)
73
+
74
+ # Put assoc back in the store
75
+ @store.store_association(server_url, assoc)
76
+
77
+ # More recent and expires after assoc
78
+ assoc2 = _gen_assoc(issued=1)
79
+ @store.store_association(server_url, assoc2)
80
+
81
+ # After storing an association with a different handle, but the
82
+ # same server_url, the handle with the later expiration is returned.
83
+ _check_retrieve(server_url, nil, assoc2)
84
+
85
+ # We can still retrieve the older association
86
+ _check_retrieve(server_url, assoc.handle, assoc)
87
+
88
+ # Plus we can retrieve the association with the later expiration
89
+ # explicitly
90
+ _check_retrieve(server_url, assoc2.handle, assoc2)
91
+
92
+ # More recent, and expires earlier than assoc2 or assoc. Make sure
93
+ # that we're picking the one with the latest issued date and not
94
+ # taking into account the expiration.
95
+ assoc3 = _gen_assoc(issued=2, lifetime=100)
96
+ @store.store_association(server_url, assoc3)
97
+
98
+ _check_retrieve(server_url, nil, assoc3)
99
+ _check_retrieve(server_url, assoc.handle, assoc)
100
+ _check_retrieve(server_url, assoc2.handle, assoc2)
101
+ _check_retrieve(server_url, assoc3.handle, assoc3)
102
+
103
+ _check_remove(server_url, assoc2.handle, true)
104
+
105
+ _check_retrieve(server_url, nil, assoc3)
106
+ _check_retrieve(server_url, assoc.handle, assoc)
107
+ _check_retrieve(server_url, assoc2.handle, nil)
108
+ _check_retrieve(server_url, assoc3.handle, assoc3)
109
+
110
+ _check_remove(server_url, assoc2.handle, false)
111
+ _check_remove(server_url, assoc3.handle, true)
112
+
113
+ ret_assoc = @store.get_association(server_url, nil)
114
+ unexpected = [assoc2.handle, assoc3.handle]
115
+ assert(ret_assoc.nil? || !unexpected.member?(ret_assoc.handle),
116
+ "correct association")
117
+
118
+ _check_retrieve(server_url, assoc.handle, assoc)
119
+ _check_retrieve(server_url, assoc2.handle, nil)
120
+ _check_retrieve(server_url, assoc3.handle, nil)
121
+
122
+ _check_remove(server_url, assoc2.handle, false)
123
+ _check_remove(server_url, assoc.handle, true)
124
+ _check_remove(server_url, assoc3.handle, false)
125
+
126
+ _check_retrieve(server_url, nil, nil)
127
+ _check_retrieve(server_url, assoc.handle, nil)
128
+ _check_retrieve(server_url, assoc2.handle, nil)
129
+ _check_retrieve(server_url, assoc3.handle, nil)
130
+
131
+ _check_remove(server_url, assoc2.handle, false)
132
+ _check_remove(server_url, assoc.handle, false)
133
+ _check_remove(server_url, assoc3.handle, false)
134
+ end
135
+
136
+ def test_assoc_cleanup
137
+ assocValid1 = _gen_assoc(-3600, 7200)
138
+ assocValid2 = _gen_assoc(-5)
139
+ assocExpired1 = _gen_assoc(-7200, 3600)
140
+ assocExpired2 = _gen_assoc(-7200, 3600)
141
+
142
+ @store.cleanup_associations
143
+ @store.store_association(server_url + '1', assocValid1)
144
+ @store.store_association(server_url + '1', assocExpired1)
145
+ @store.store_association(server_url + '2', assocExpired2)
146
+ @store.store_association(server_url + '3', assocValid2)
147
+
148
+ cleaned = @store.cleanup_associations()
149
+ assert_equal(2, cleaned, "cleaned up associations")
150
+ end
151
+
152
+ def _check_use_nonce(nonce, expected, server_url, msg='')
153
+ stamp, salt = Nonce::split_nonce(nonce)
154
+ actual = @store.use_nonce(server_url, stamp, salt)
155
+ assert_equal(expected, actual, msg)
156
+ end
157
+
158
+ def server_url
159
+ "http://www.myopenid.com/openid"
160
+ end
161
+
162
+ def test_nonce
163
+ [server_url, ''].each{|url|
164
+ nonce1 = Nonce::mk_nonce
165
+
166
+ _check_use_nonce(nonce1, true, url, "#{url}: nonce allowed by default")
167
+ _check_use_nonce(nonce1, false, url, "#{url}: nonce not allowed twice")
168
+ _check_use_nonce(nonce1, false, url, "#{url}: nonce not allowed third time")
169
+
170
+ # old nonces shouldn't pass
171
+ old_nonce = Nonce::mk_nonce(3600)
172
+ _check_use_nonce(old_nonce, false, url, "Old nonce #{old_nonce.inspect} passed")
173
+
174
+ }
175
+ end
176
+
177
+ def test_nonce_cleanup
178
+ now = Time.now.to_i
179
+ old_nonce1 = Nonce::mk_nonce(now - 20000)
180
+ old_nonce2 = Nonce::mk_nonce(now - 10000)
181
+ recent_nonce = Nonce::mk_nonce(now - 600)
182
+
183
+ orig_skew = Nonce.skew
184
+ Nonce.skew = 0
185
+ count = @store.cleanup_nonces
186
+ Nonce.skew = 1000000
187
+ ts, salt = Nonce::split_nonce(old_nonce1)
188
+ assert(@store.use_nonce(server_url, ts, salt), "oldnonce1")
189
+ ts, salt = Nonce::split_nonce(old_nonce2)
190
+ assert(@store.use_nonce(server_url, ts, salt), "oldnonce2")
191
+ ts, salt = Nonce::split_nonce(recent_nonce)
192
+ assert(@store.use_nonce(server_url, ts, salt), "recent_nonce")
193
+
194
+ Nonce.skew = 1000
195
+ cleaned = @store.cleanup_nonces
196
+ assert_equal(2, cleaned, "Cleaned #{cleaned} nonces")
197
+
198
+ Nonce.skew = 100000
199
+ ts, salt = Nonce::split_nonce(old_nonce1)
200
+ assert(@store.use_nonce(server_url, ts, salt), "oldnonce1 after cleanup")
201
+ ts, salt = Nonce::split_nonce(old_nonce2)
202
+ assert(@store.use_nonce(server_url, ts, salt), "oldnonce2 after cleanup")
203
+ ts, salt = Nonce::split_nonce(recent_nonce)
204
+ assert(!@store.use_nonce(server_url, ts, salt), "recent_nonce after cleanup")
205
+
206
+ Nonce.skew = orig_skew
207
+
208
+ end
209
+ end
210
+
211
+ class SequelStoreTestCase < Test::Unit::TestCase
212
+ include StoreTestCase
213
+
214
+ def setup
215
+ @store = OpenID::Store::Sequel.new
216
+ end
217
+ end
218
+ end
219
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: openid-store-sequel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dylan Egan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ruby-openid
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: sequel
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: pg
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: sqlite3
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Storing your OpenIDs in your Sequels.
95
+ email:
96
+ - dylanegan@gmail.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - .gitignore
102
+ - .travis.yml
103
+ - Gemfile
104
+ - Makefile
105
+ - README.md
106
+ - Rakefile
107
+ - cat-attack.gif
108
+ - lib/openid/store/sequel.rb
109
+ - lib/openid/store/sequel/association.rb
110
+ - lib/openid/store/sequel/nonce.rb
111
+ - lib/openid/store/sequel/version.rb
112
+ - openid-store-sequel.gemspec
113
+ - test/helper.rb
114
+ - test/openid/store/sequel_test.rb
115
+ homepage: ''
116
+ licenses: []
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ! '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 1.8.21
136
+ signing_key:
137
+ specification_version: 3
138
+ summary: Storing OpenIDs in Sequels.
139
+ test_files:
140
+ - test/helper.rb
141
+ - test/openid/store/sequel_test.rb