openid_active_record_store 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,63 @@
1
+ = openid_active_record_store
2
+
3
+ http://rubygems.org/gems/openid_active_record_store
4
+ {Project}[http://rubygems.org/gems/openid_active_record_store]
5
+ {Wiki}[http://wiki.github.com/raggi/openid_active_record_store/]
6
+ {Source Code}[http://github.com/raggi/openid_active_record_store/]
7
+ {Issues}[http://github.com/raggi/openid_active_record_store/issues]
8
+
9
+ == DESCRIPTION:
10
+
11
+ A rails engine for OpenID/Omniauth that writes to ActiveRecord for the OpenID data. Forked from an old project by Kazuyoshi Tlacaelel.
12
+
13
+ == FEATURES/PROBLEMS:
14
+
15
+ * Simple
16
+ * Lowish test coverage
17
+ * Binary column may not work well with some adapters
18
+
19
+ == SYNOPSIS:
20
+
21
+ rake openid_active_record_store:install:migrations
22
+ rake db:migrate
23
+
24
+ # Omniauth example:
25
+ Rails.application.config.middleware.use(
26
+ OmniAuth::Strategies::GoogleApps,
27
+ OpenID::Store::ActiveRecord.new,
28
+ { :name => 'example', :domain => 'example.org' }
29
+ )
30
+
31
+ == REQUIREMENTS:
32
+
33
+ * Rails 3+
34
+ * OpenID
35
+
36
+ == INSTALL:
37
+
38
+ * gem install openid_active_record_store
39
+
40
+ == LICENSE:
41
+
42
+ (The MIT License)
43
+
44
+ Copyright (c) 2011 Kazuyoshi Tlacaelel, James Tucker, Wildfire Interactive Inc
45
+
46
+ Permission is hereby granted, free of charge, to any person obtaining
47
+ a copy of this software and associated documentation files (the
48
+ 'Software'), to deal in the Software without restriction, including
49
+ without limitation the rights to use, copy, modify, merge, publish,
50
+ distribute, sublicense, and/or sell copies of the Software, and to
51
+ permit persons to whom the Software is furnished to do so, subject to
52
+ the following conditions:
53
+
54
+ The above copyright notice and this permission notice shall be
55
+ included in all copies or substantial portions of the Software.
56
+
57
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
58
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
59
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
60
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
61
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
62
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
63
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env rake
2
+ require 'rake/testtask'
3
+ require 'rdoc/task'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the openid_store_active_record plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.ruby_opts << '-rubygems'
11
+ t.libs += %w[test]
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ t.warning = true
15
+ end
16
+
17
+ desc 'Generate documentation for the openid_store_active_record plugin.'
18
+ RDoc::Task.new(:rdoc) do |rdoc|
19
+ rdoc.rdoc_dir = 'rdoc'
20
+ rdoc.title = 'OpenidStoreActiveRecord'
21
+ rdoc.options << '--line-numbers' << '--inline-source'
22
+ rdoc.rdoc_files.include('README')
23
+ rdoc.rdoc_files.include('lib/**/*.rb')
24
+ end
25
+
26
+ desc "build gem"
27
+ task :gem do
28
+ sh "gem build openid_active_record_store.gemspec"
29
+ end
@@ -0,0 +1,5 @@
1
+ require 'active_record'
2
+
3
+ class OpenidAbstract < ActiveRecord::Base
4
+ self.abstract_class = true
5
+ end
@@ -0,0 +1,2 @@
1
+ class OpenidAssociation < OpenidAbstract
2
+ end
@@ -0,0 +1,8 @@
1
+ class OpenidNonce < OpenidAbstract
2
+
3
+ # attempt to scan timestamps (integers) first for fast access.
4
+ def self.exists_by_target?(timestamp, salt, target)
5
+ where(:timestamp => timestamp, :target => target).size > 0
6
+ end
7
+
8
+ end
@@ -0,0 +1,22 @@
1
+ class CreateOpenidAssociations < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ create_table :openid_associations do |t|
5
+ t.datetime :issued_at
6
+ t.integer :lifetime
7
+ t.string :assoc_type
8
+ t.text :handle
9
+ t.binary :secret
10
+
11
+ t.string :target, :size => 32
12
+ t.text :server_url
13
+
14
+ t.timestamps
15
+ end
16
+ end
17
+
18
+ def self.down
19
+ drop_table :openid_associations
20
+ end
21
+
22
+ end
@@ -0,0 +1,17 @@
1
+ class CreateOpenidNonces < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ create_table :openid_nonces do |t|
5
+ t.integer :timestamp
6
+ t.string :salt
7
+ t.string :target, :size => 32
8
+ t.text :server_url
9
+ t.timestamps
10
+ end
11
+ end
12
+
13
+ def self.down
14
+ drop_table :openid_nonces
15
+ end
16
+
17
+ end
@@ -0,0 +1,121 @@
1
+ require 'openid/util'
2
+ require 'openid/store/interface'
3
+ require 'openid/association'
4
+ require 'openssl'
5
+
6
+ module OpenID
7
+ module Store
8
+ class ActiveRecord < Interface
9
+
10
+ # Put a Association object into storage.
11
+ # When implementing a store, don't assume that there are any limitations
12
+ # on the character set of the server_url. In particular, expect to see
13
+ # unescaped non-url-safe characters in the server_url field.
14
+ def store_association(server_url, association)
15
+ OpenidAssociation.create!(
16
+ :server_url => server_url,
17
+ :target => targetize(server_url),
18
+ :handle => association.handle,
19
+ :secret => association.secret,
20
+ :issued_at => association.issued,
21
+ :lifetime => association.lifetime,
22
+ :assoc_type => association.assoc_type
23
+ )
24
+ true
25
+ end
26
+
27
+ # Returns a Association object from storage that matches
28
+ # the server_url. Returns nil if no such association is found or if
29
+ # the one matching association is expired. (Is allowed to GC expired
30
+ # associations when found.)
31
+ def get_association(server_url, handle=nil)
32
+ oas = OpenidAssociation.find_all_by_target targetize(server_url)
33
+ return nil if oas.empty?
34
+ unless handle.nil?
35
+ return nil unless oas.collect(&:handle).include? handle
36
+ return build_association(oas.find { |oa| oa.handle == handle })
37
+ end
38
+ oas.sort_by(&:issued_at).collect { |oa| build_association(oa) }.last
39
+ end
40
+
41
+ # If there is a matching association, remove it from the store and
42
+ # return true, otherwise return false.
43
+ def remove_association(server_url, handle)
44
+ oas = OpenidAssociation.find_all_by_target targetize(server_url)
45
+ return false unless oas.collect(&:handle).include? handle
46
+ oas.find_all { |oa| oa.handle == handle }.each(&:delete).size > 0
47
+ end
48
+
49
+ # Return true if the nonce has not been used before, and store it
50
+ # for a while to make sure someone doesn't try to use the same value
51
+ # again. Return false if the nonce has already been used or if the
52
+ # timestamp is not current.
53
+ # You can use OpenID::Store::Nonce::SKEW for your timestamp window.
54
+ # server_url: URL of the server from which the nonce originated
55
+ # timestamp: time the nonce was created in seconds since unix epoch
56
+ # salt: A random string that makes two nonces issued by a server in
57
+ # the same second unique
58
+ def use_nonce(server_url, timestamp, salt)
59
+ return false if (timestamp - Time.now.to_i).abs > Nonce.skew
60
+ params = [timestamp, salt, targetize(server_url)]
61
+ return false if OpenidNonce.exists_by_target?(*params)
62
+ return create_nonce(server_url, timestamp, salt)
63
+ end
64
+
65
+ # Remove expired nonces and associations from the store
66
+ # Not called during normal library operation, this method is for store
67
+ # admins to keep their storage from filling up with expired data
68
+ def cleanup
69
+ cleanup_nonces
70
+ cleanup_associations
71
+ end
72
+
73
+ # Remove expired associations from the store
74
+ # Not called during normal library operation, this method is for store
75
+ # admins to keep their storage from filling up with expired data
76
+ def cleanup_associations
77
+ oas = OpenidAssociation.all.collect do |oa|
78
+ oa.id if build_association(oa).expires_in == 0
79
+ end
80
+ OpenidAssociation.delete oas.compact
81
+ end
82
+
83
+ # Remove expired nonces from the store
84
+ # Discards any nonce that is old enough that it wouldn't pass use_nonce
85
+ # Not called during normal library operation, this method is for store
86
+ # admins to keep their storage from filling up with expired data
87
+ def cleanup_nonces
88
+ now = Time.now.to_i
89
+ nonces = OpenidNonce.all
90
+ ids = nonces.collect { |n| n.id if (n.timestamp - now).abs > Nonce.skew }
91
+ OpenidNonce.delete ids.compact
92
+ end
93
+
94
+ private
95
+
96
+ def targetize(server_url)
97
+ OpenSSL::Digest::MD5.hexdigest(server_url)
98
+ end
99
+
100
+ def build_association(open_id_association)
101
+ OpenID::Association.new(
102
+ open_id_association.handle,
103
+ open_id_association.secret,
104
+ open_id_association.issued_at,
105
+ open_id_association.lifetime,
106
+ open_id_association.assoc_type
107
+ )
108
+ end
109
+
110
+ def create_nonce(server_url, timestamp, salt)
111
+ OpenidNonce.create!(
112
+ :target => targetize(server_url),
113
+ :server_url => server_url,
114
+ :timestamp => timestamp
115
+ )
116
+ true
117
+ end
118
+
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,33 @@
1
+ require 'rails/engine'
2
+ require 'openid/store/active_record'
3
+
4
+ module OpenidActiveRecordStore
5
+ class Engine < Rails::Engine
6
+ config.eager_load_paths << File.expand_path("../../app/models", __FILE__)
7
+ end
8
+ class Railtie < Rails::Railtie
9
+ rake_tasks do
10
+ namespace :openid_active_record_store do
11
+ namespace :install do
12
+
13
+ files = File.expand_path("../../db/migrate/*.rb", __FILE__)
14
+ sources = FileList[files]
15
+ targets = sources.map do |source|
16
+ ts = Time.now.to_f.to_s.sub('.', '')
17
+ "db/migrate/#{ts}_#{File.basename(source)}"
18
+ end
19
+
20
+ desc "install migrations"
21
+ task :migrations => targets
22
+
23
+ sources.zip(targets).each do |source, target|
24
+ file target => source do
25
+ cp source, target
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end unless Gem::Version.new(Rails.version) >= Gem::Version.new('3.1.0')
32
+ end
33
+ end
@@ -0,0 +1,237 @@
1
+ require 'test_helper'
2
+ require 'openid/store/nonce'
3
+
4
+ class OpenidStoreActiveRecordTest < ActiveSupport::TestCase
5
+
6
+ # ============================================================================
7
+ # TESTING SCENARIO
8
+ # ============================================================================
9
+
10
+ setup :prepare_scenario, :clean_tables
11
+ teardown :destroy_scenario
12
+
13
+ def prepare_scenario
14
+ @store = OpenID::Store::ActiveRecord.new
15
+ @@allowed_nonce = '0123456789abcdefghijklmnopqrst' +
16
+ 'uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
17
+ @@allowed_handle = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ' +
18
+ 'RSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
19
+ end
20
+
21
+ def clean_tables
22
+ # super duper make sure talbes are empty
23
+ OpenidAssociation.all.each { |ao| ao.destroy }
24
+ OpenidNonce.all.each { |nonce| nonce.destroy }
25
+ end
26
+
27
+ def destroy_scenario
28
+ @@allowed_handle = @@allowed_nonce = @store = nil
29
+ end
30
+
31
+ # ============================================================================
32
+ # TESTS BELOW BROUGHT FROM THE ORIGINAL 'ruby-openid' (store) test suite
33
+ # these methods test the association interactivity with a store object
34
+ # ============================================================================
35
+
36
+ def _gen_secret(n, chars=nil)
37
+ OpenID::CryptUtil.random_string(n, chars)
38
+ end
39
+
40
+ def _gen_handle(n)
41
+ OpenID::CryptUtil.random_string(n, @@allowed_handle)
42
+ end
43
+
44
+ def _gen_assoc(issued_at, lifetime=600)
45
+ secret = _gen_secret(20)
46
+ handle = _gen_handle(128)
47
+ OpenID::Association.new(handle, secret, Time.now + issued_at, lifetime,
48
+ 'HMAC-SHA1')
49
+ end
50
+
51
+ def _check_retrieve(url, handle=nil, expected=nil)
52
+ ret_assoc = @store.get_association(url, handle)
53
+
54
+ if expected.nil?
55
+ assert_nil(ret_assoc)
56
+ else
57
+ %w[assoc_type handle issued lifetime secret].each do |prop|
58
+ ex, actual = expected.send(prop), ret_assoc.send(prop)
59
+ if ex.kind_of?(Time)
60
+ ex, actual = ex.to_i, actual.to_i
61
+ end
62
+ assert_equal ex, actual, "#{prop} doesn't match"
63
+ end
64
+ end
65
+ end
66
+
67
+ def _check_remove(url, handle, expected)
68
+ present = @store.remove_association(url, handle)
69
+ assert_equal(expected, present)
70
+ end
71
+
72
+ def test_store
73
+ server_url = "http://www.myopenid.com/openid"
74
+ assoc = _gen_assoc(issued_at=0)
75
+
76
+ # Make sure that a missing association returns no result
77
+ _check_retrieve(server_url)
78
+
79
+ # Check that after storage, getting returns the same result
80
+ @store.store_association(server_url, assoc)
81
+ _check_retrieve(server_url, nil, assoc)
82
+
83
+ # more than once
84
+ _check_retrieve(server_url, nil, assoc)
85
+
86
+ # Storing more than once has no ill effect
87
+ @store.store_association(server_url, assoc)
88
+ _check_retrieve(server_url, nil, assoc)
89
+
90
+ # Removing an association that does not exist returns not present
91
+ _check_remove(server_url, assoc.handle + 'x', false)
92
+
93
+ # Removing an association that does not exist returns not present
94
+ _check_remove(server_url + 'x', assoc.handle, false)
95
+
96
+ # Removing an association that is present returns present
97
+ _check_remove(server_url, assoc.handle, true)
98
+
99
+ # but not present on subsequent calls
100
+ _check_remove(server_url, assoc.handle, false)
101
+
102
+ # Put assoc back in the store
103
+ @store.store_association(server_url, assoc)
104
+
105
+ # More recent and expires after assoc
106
+ assoc2 = _gen_assoc(issued_at=1)
107
+ @store.store_association(server_url, assoc2)
108
+
109
+ # After storing an association with a different handle, but the
110
+ # same server_url, the handle with the later expiration is returned.
111
+ _check_retrieve(server_url, nil, assoc2)
112
+
113
+ # We can still retrieve the older association
114
+ _check_retrieve(server_url, assoc.handle, assoc)
115
+
116
+ # Plus we can retrieve the association with the later expiration
117
+ # explicitly
118
+ _check_retrieve(server_url, assoc2.handle, assoc2)
119
+
120
+ # More recent, and expires earlier than assoc2 or assoc. Make sure
121
+ # that we're picking the one with the latest issued date and not
122
+ # taking into account the expiration.
123
+ assoc3 = _gen_assoc(issued_at=2, lifetime=100)
124
+ @store.store_association(server_url, assoc3)
125
+
126
+ _check_retrieve(server_url, nil, assoc3)
127
+ _check_retrieve(server_url, assoc.handle, assoc)
128
+ _check_retrieve(server_url, assoc2.handle, assoc2)
129
+ _check_retrieve(server_url, assoc3.handle, assoc3)
130
+
131
+ _check_remove(server_url, assoc2.handle, true)
132
+
133
+ _check_retrieve(server_url, nil, assoc3)
134
+ _check_retrieve(server_url, assoc.handle, assoc)
135
+ _check_retrieve(server_url, assoc2.handle, nil)
136
+ _check_retrieve(server_url, assoc3.handle, assoc3)
137
+
138
+ _check_remove(server_url, assoc2.handle, false)
139
+ _check_remove(server_url, assoc3.handle, true)
140
+
141
+ _check_retrieve(server_url, nil, assoc)
142
+ _check_retrieve(server_url, assoc.handle, assoc)
143
+ _check_retrieve(server_url, assoc2.handle, nil)
144
+ _check_retrieve(server_url, assoc3.handle, nil)
145
+
146
+ _check_remove(server_url, assoc2.handle, false)
147
+ _check_remove(server_url, assoc.handle, true)
148
+ _check_remove(server_url, assoc3.handle, false)
149
+
150
+ _check_retrieve(server_url, nil, nil)
151
+ _check_retrieve(server_url, assoc.handle, nil)
152
+ _check_retrieve(server_url, assoc2.handle, nil)
153
+ _check_retrieve(server_url, assoc3.handle, nil)
154
+
155
+ _check_remove(server_url, assoc2.handle, false)
156
+ _check_remove(server_url, assoc.handle, false)
157
+ _check_remove(server_url, assoc3.handle, false)
158
+
159
+ assocValid1 = _gen_assoc(-3600, 7200)
160
+ assocValid2 = _gen_assoc(-5)
161
+ assocExpired1 = _gen_assoc(-7200, 3600)
162
+ assocExpired2 = _gen_assoc(-7200, 3600)
163
+
164
+ @store.cleanup_associations
165
+ @store.store_association(server_url + '1', assocValid1)
166
+ @store.store_association(server_url + '1', assocExpired1)
167
+ @store.store_association(server_url + '2', assocExpired2)
168
+ @store.store_association(server_url + '3', assocValid2)
169
+
170
+ cleaned = @store.cleanup_associations()
171
+ assert_equal(2, cleaned, "cleaned up associations")
172
+ end
173
+
174
+ # ============================================================================
175
+ # am including open id here because the nonce module is not being mocked
176
+ # ============================================================================
177
+ include OpenID
178
+
179
+ # ============================================================================
180
+ # TESTS BELOW BROUGHT FROM THE ORIGINAL 'ruby-openid' (store) test suite
181
+ # these methods test the nonce interactivity with a store object
182
+ # ============================================================================
183
+
184
+ def _check_use_nonce(nonce, expected, server_url, msg='')
185
+ stamp, salt = Nonce::split_nonce(nonce)
186
+ actual = @store.use_nonce(server_url, stamp, salt)
187
+ assert_equal(expected, actual, msg)
188
+ end
189
+
190
+ def test_nonce
191
+ server_url = "http://www.myopenid.com/openid"
192
+ [server_url, ''].each{|url|
193
+ nonce1 = Nonce::mk_nonce
194
+
195
+ _check_use_nonce(nonce1, true, url, "#{url}: nonce allowed by default")
196
+ _check_use_nonce(nonce1, false, url, "#{url}: nonce not allowed twice")
197
+ _check_use_nonce(nonce1, false, url, "#{url}: nonce not allowed third time")
198
+
199
+ # old nonces shouldn't pass
200
+ old_nonce = Nonce::mk_nonce(3600)
201
+ _check_use_nonce(old_nonce, false, url, "Old nonce #{old_nonce.inspect} passed")
202
+
203
+ }
204
+
205
+ now = Time.now.to_i
206
+ old_nonce1 = Nonce::mk_nonce(now - 20000)
207
+ old_nonce2 = Nonce::mk_nonce(now - 10000)
208
+ recent_nonce = Nonce::mk_nonce(now - 600)
209
+
210
+ orig_skew = Nonce.skew
211
+ Nonce.skew = 0
212
+ count = @store.cleanup_nonces
213
+ Nonce.skew = 1000000
214
+ ts, salt = Nonce::split_nonce(old_nonce1)
215
+ assert(@store.use_nonce(server_url, ts, salt), "oldnonce1")
216
+ ts, salt = Nonce::split_nonce(old_nonce2)
217
+ assert(@store.use_nonce(server_url, ts, salt), "oldnonce2")
218
+ ts, salt = Nonce::split_nonce(recent_nonce)
219
+ assert(@store.use_nonce(server_url, ts, salt), "recent_nonce")
220
+
221
+
222
+ Nonce.skew = 1000
223
+ cleaned = @store.cleanup_nonces
224
+ assert_equal(2, cleaned, "Cleaned #{cleaned} nonces")
225
+
226
+ Nonce.skew = 100000
227
+ ts, salt = Nonce::split_nonce(old_nonce1)
228
+ assert(@store.use_nonce(server_url, ts, salt), "oldnonce1 after cleanup")
229
+ ts, salt = Nonce::split_nonce(old_nonce2)
230
+ assert(@store.use_nonce(server_url, ts, salt), "oldnonce2 after cleanup")
231
+ ts, salt = Nonce::split_nonce(recent_nonce)
232
+ assert(!@store.use_nonce(server_url, ts, salt), "recent_nonce after cleanup")
233
+
234
+ Nonce.skew = orig_skew
235
+ end
236
+
237
+ end
@@ -0,0 +1,24 @@
1
+ require 'test/unit'
2
+ require 'openid_active_record_store'
3
+ require 'active_record'
4
+
5
+ db = {
6
+ :adapter => :mysql2,
7
+ :database => 'openid_active_record_store'
8
+ }
9
+
10
+ # XXX yes, there are better ways. patches please!
11
+
12
+ system "echo 'drop database #{db[:database]};' | mysql5 -uroot" rescue nil
13
+ system "echo 'create database #{db[:database]};' | mysql5 -uroot"
14
+
15
+ ActiveRecord::Base.establish_connection db
16
+
17
+ Dir['app/models/*.rb'].each do |model|
18
+ require File.expand_path(model)
19
+ end
20
+
21
+ Dir['db/migrations/*.rb'].each do |migration|
22
+ require migration
23
+ Object.const_get(File.basename(migration, '.rb').camelize).up
24
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: openid_active_record_store
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - James Tucker
14
+ - Kazuyoshi Tlacaelel
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2011-05-24 00:00:00 -07:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: rails
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 5
31
+ segments:
32
+ - 3
33
+ version: "3"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ description: An ActiveRecord store for OpenID, forked from its original author for rails 3 support
37
+ email: info@wildfireapp.com
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ files:
45
+ - README.rdoc
46
+ - Rakefile
47
+ - app/models/openid_abstract.rb
48
+ - app/models/openid_association.rb
49
+ - app/models/openid_nonce.rb
50
+ - db/migrate/create_openid_associations.rb
51
+ - db/migrate/create_openid_nonces.rb
52
+ - lib/openid/store/active_record.rb
53
+ - lib/openid_active_record_store.rb
54
+ - test/openid_store_active_record_test.rb
55
+ - test/test_helper.rb
56
+ has_rdoc: true
57
+ homepage: http://github.com/wildfireapp/openid_active_record_store
58
+ licenses:
59
+ - MIT
60
+ post_install_message:
61
+ rdoc_options: []
62
+
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 3
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.5.2
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: An ActiveRecord store for OpenID
90
+ test_files: []
91
+