czak-authlogic-oid 1.0.4
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.
- data/CHANGELOG.rdoc +25 -0
- data/MIT-LICENSE +20 -0
- data/Manifest.txt +38 -0
- data/README.rdoc +93 -0
- data/Rakefile +21 -0
- data/init.rb +1 -0
- data/lib/authlogic_openid/acts_as_authentic.rb +169 -0
- data/lib/authlogic_openid/session.rb +96 -0
- data/lib/authlogic_openid/version.rb +51 -0
- data/lib/authlogic_openid.rb +6 -0
- data/test/acts_as_authentic_test.rb +105 -0
- data/test/fixtures/users.yml +9 -0
- data/test/libs/open_id_authentication/CHANGELOG +35 -0
- data/test/libs/open_id_authentication/README +231 -0
- data/test/libs/open_id_authentication/Rakefile +22 -0
- data/test/libs/open_id_authentication/generators/open_id_authentication_tables/open_id_authentication_tables_generator.rb +11 -0
- data/test/libs/open_id_authentication/generators/open_id_authentication_tables/templates/migration.rb +20 -0
- data/test/libs/open_id_authentication/generators/upgrade_open_id_authentication_tables/templates/migration.rb +26 -0
- data/test/libs/open_id_authentication/generators/upgrade_open_id_authentication_tables/upgrade_open_id_authentication_tables_generator.rb +11 -0
- data/test/libs/open_id_authentication/init.rb +18 -0
- data/test/libs/open_id_authentication/lib/open_id_authentication/association.rb +9 -0
- data/test/libs/open_id_authentication/lib/open_id_authentication/db_store.rb +55 -0
- data/test/libs/open_id_authentication/lib/open_id_authentication/mem_cache_store.rb +73 -0
- data/test/libs/open_id_authentication/lib/open_id_authentication/nonce.rb +5 -0
- data/test/libs/open_id_authentication/lib/open_id_authentication/request.rb +23 -0
- data/test/libs/open_id_authentication/lib/open_id_authentication/timeout_fixes.rb +20 -0
- data/test/libs/open_id_authentication/lib/open_id_authentication.rb +244 -0
- data/test/libs/open_id_authentication/tasks/open_id_authentication_tasks.rake +30 -0
- data/test/libs/open_id_authentication/test/mem_cache_store_test.rb +151 -0
- data/test/libs/open_id_authentication/test/normalize_test.rb +32 -0
- data/test/libs/open_id_authentication/test/open_id_authentication_test.rb +46 -0
- data/test/libs/open_id_authentication/test/status_test.rb +14 -0
- data/test/libs/open_id_authentication/test/test_helper.rb +17 -0
- data/test/libs/rails_trickery.rb +41 -0
- data/test/libs/user.rb +3 -0
- data/test/libs/user_session.rb +2 -0
- data/test/session_test.rb +32 -0
- data/test/test_helper.rb +78 -0
- metadata +115 -0
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'openid/extensions/sreg'
|
3
|
+
require 'openid/extensions/ax'
|
4
|
+
require 'openid/store/filesystem'
|
5
|
+
|
6
|
+
require File.dirname(__FILE__) + '/open_id_authentication/association'
|
7
|
+
require File.dirname(__FILE__) + '/open_id_authentication/nonce'
|
8
|
+
require File.dirname(__FILE__) + '/open_id_authentication/db_store'
|
9
|
+
require File.dirname(__FILE__) + '/open_id_authentication/mem_cache_store'
|
10
|
+
require File.dirname(__FILE__) + '/open_id_authentication/request'
|
11
|
+
require File.dirname(__FILE__) + '/open_id_authentication/timeout_fixes' if OpenID::VERSION == "2.0.4"
|
12
|
+
|
13
|
+
module OpenIdAuthentication
|
14
|
+
OPEN_ID_AUTHENTICATION_DIR = RAILS_ROOT + "/tmp/openids"
|
15
|
+
|
16
|
+
def self.store
|
17
|
+
@@store
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.store=(*store_option)
|
21
|
+
store, *parameters = *([ store_option ].flatten)
|
22
|
+
|
23
|
+
@@store = case store
|
24
|
+
when :db
|
25
|
+
OpenIdAuthentication::DbStore.new
|
26
|
+
when :mem_cache
|
27
|
+
OpenIdAuthentication::MemCacheStore.new(*parameters)
|
28
|
+
when :file
|
29
|
+
OpenID::Store::Filesystem.new(OPEN_ID_AUTHENTICATION_DIR)
|
30
|
+
else
|
31
|
+
raise "Unknown store: #{store}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
self.store = :db
|
36
|
+
|
37
|
+
class InvalidOpenId < StandardError
|
38
|
+
end
|
39
|
+
|
40
|
+
class Result
|
41
|
+
ERROR_MESSAGES = {
|
42
|
+
:missing => "Sorry, the OpenID server couldn't be found",
|
43
|
+
:invalid => "Sorry, but this does not appear to be a valid OpenID",
|
44
|
+
:canceled => "OpenID verification was canceled",
|
45
|
+
:failed => "OpenID verification failed",
|
46
|
+
:setup_needed => "OpenID verification needs setup"
|
47
|
+
}
|
48
|
+
|
49
|
+
def self.[](code)
|
50
|
+
new(code)
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(code)
|
54
|
+
@code = code
|
55
|
+
end
|
56
|
+
|
57
|
+
def status
|
58
|
+
@code
|
59
|
+
end
|
60
|
+
|
61
|
+
ERROR_MESSAGES.keys.each { |state| define_method("#{state}?") { @code == state } }
|
62
|
+
|
63
|
+
def successful?
|
64
|
+
@code == :successful
|
65
|
+
end
|
66
|
+
|
67
|
+
def unsuccessful?
|
68
|
+
ERROR_MESSAGES.keys.include?(@code)
|
69
|
+
end
|
70
|
+
|
71
|
+
def message
|
72
|
+
ERROR_MESSAGES[@code]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# normalizes an OpenID according to http://openid.net/specs/openid-authentication-2_0.html#normalization
|
77
|
+
def self.normalize_identifier(identifier)
|
78
|
+
# clean up whitespace
|
79
|
+
identifier = identifier.to_s.strip
|
80
|
+
|
81
|
+
# if an XRI has a prefix, strip it.
|
82
|
+
identifier.gsub!(/xri:\/\//i, '')
|
83
|
+
|
84
|
+
# dodge XRIs -- TODO: validate, don't just skip.
|
85
|
+
unless ['=', '@', '+', '$', '!', '('].include?(identifier.at(0))
|
86
|
+
# does it begin with http? if not, add it.
|
87
|
+
identifier = "http://#{identifier}" unless identifier =~ /^http/i
|
88
|
+
|
89
|
+
# strip any fragments
|
90
|
+
identifier.gsub!(/\#(.*)$/, '')
|
91
|
+
|
92
|
+
begin
|
93
|
+
uri = URI.parse(identifier)
|
94
|
+
uri.scheme = uri.scheme.downcase # URI should do this
|
95
|
+
identifier = uri.normalize.to_s
|
96
|
+
rescue URI::InvalidURIError
|
97
|
+
raise InvalidOpenId.new("#{identifier} is not an OpenID identifier")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
return identifier
|
102
|
+
end
|
103
|
+
|
104
|
+
# deprecated for OpenID 2.0, where not all OpenIDs are URLs
|
105
|
+
def self.normalize_url(url)
|
106
|
+
ActiveSupport::Deprecation.warn "normalize_url has been deprecated, use normalize_identifier instead"
|
107
|
+
self.normalize_identifier(url)
|
108
|
+
end
|
109
|
+
|
110
|
+
protected
|
111
|
+
def normalize_url(url)
|
112
|
+
OpenIdAuthentication.normalize_url(url)
|
113
|
+
end
|
114
|
+
|
115
|
+
def normalize_identifier(url)
|
116
|
+
OpenIdAuthentication.normalize_identifier(url)
|
117
|
+
end
|
118
|
+
|
119
|
+
# The parameter name of "openid_identifier" is used rather than the Rails convention "open_id_identifier"
|
120
|
+
# because that's what the specification dictates in order to get browser auto-complete working across sites
|
121
|
+
def using_open_id?(identity_url = nil) #:doc:
|
122
|
+
identity_url ||= params[:openid_identifier] || params[:openid_url]
|
123
|
+
!identity_url.blank? || params[:open_id_complete]
|
124
|
+
end
|
125
|
+
|
126
|
+
def authenticate_with_open_id(identity_url = nil, options = {}, &block) #:doc:
|
127
|
+
identity_url ||= params[:openid_identifier] || params[:openid_url]
|
128
|
+
|
129
|
+
if params[:open_id_complete].nil?
|
130
|
+
begin_open_id_authentication(identity_url, options, &block)
|
131
|
+
else
|
132
|
+
complete_open_id_authentication(&block)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
def begin_open_id_authentication(identity_url, options = {})
|
138
|
+
identity_url = normalize_identifier(identity_url)
|
139
|
+
return_to = options.delete(:return_to)
|
140
|
+
method = options.delete(:method)
|
141
|
+
|
142
|
+
options[:required] ||= [] # reduces validation later
|
143
|
+
options[:optional] ||= []
|
144
|
+
|
145
|
+
open_id_request = open_id_consumer.begin(identity_url)
|
146
|
+
add_simple_registration_fields(open_id_request, options)
|
147
|
+
add_ax_fields(open_id_request, options)
|
148
|
+
|
149
|
+
redirect_to(open_id_redirect_url(open_id_request, return_to, method))
|
150
|
+
rescue OpenIdAuthentication::InvalidOpenId => e
|
151
|
+
yield Result[:invalid], identity_url, nil
|
152
|
+
rescue OpenID::OpenIDError, Timeout::Error => e
|
153
|
+
logger.error("[OPENID] #{e}")
|
154
|
+
yield Result[:missing], identity_url, nil
|
155
|
+
end
|
156
|
+
|
157
|
+
def complete_open_id_authentication
|
158
|
+
params_with_path = params.reject { |key, value| request.path_parameters[key] }
|
159
|
+
params_with_path.delete(:format)
|
160
|
+
open_id_response = timeout_protection_from_identity_server { open_id_consumer.complete(params_with_path, requested_url) }
|
161
|
+
identity_url = normalize_identifier(open_id_response.display_identifier) if open_id_response.display_identifier
|
162
|
+
|
163
|
+
case open_id_response.status
|
164
|
+
when OpenID::Consumer::SUCCESS
|
165
|
+
profile_data = {}
|
166
|
+
|
167
|
+
# merge the SReg data and the AX data into a single hash of profile data
|
168
|
+
[ OpenID::SReg::Response, OpenID::AX::FetchResponse ].each do |data_response|
|
169
|
+
if data_response.from_success_response( open_id_response )
|
170
|
+
profile_data.merge! data_response.from_success_response( open_id_response ).data
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
yield Result[:successful], identity_url, profile_data
|
175
|
+
when OpenID::Consumer::CANCEL
|
176
|
+
yield Result[:canceled], identity_url, nil
|
177
|
+
when OpenID::Consumer::FAILURE
|
178
|
+
yield Result[:failed], identity_url, nil
|
179
|
+
when OpenID::Consumer::SETUP_NEEDED
|
180
|
+
yield Result[:setup_needed], open_id_response.setup_url, nil
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def open_id_consumer
|
185
|
+
OpenID::Consumer.new(session, OpenIdAuthentication.store)
|
186
|
+
end
|
187
|
+
|
188
|
+
def add_simple_registration_fields(open_id_request, fields)
|
189
|
+
sreg_request = OpenID::SReg::Request.new
|
190
|
+
|
191
|
+
# filter out AX identifiers (URIs)
|
192
|
+
required_fields = fields[:required].collect { |f| f.to_s unless f =~ /^https?:\/\// }.compact
|
193
|
+
optional_fields = fields[:optional].collect { |f| f.to_s unless f =~ /^https?:\/\// }.compact
|
194
|
+
|
195
|
+
sreg_request.request_fields(required_fields, true) unless required_fields.blank?
|
196
|
+
sreg_request.request_fields(optional_fields, false) unless optional_fields.blank?
|
197
|
+
sreg_request.policy_url = fields[:policy_url] if fields[:policy_url]
|
198
|
+
open_id_request.add_extension(sreg_request)
|
199
|
+
end
|
200
|
+
|
201
|
+
def add_ax_fields( open_id_request, fields )
|
202
|
+
ax_request = OpenID::AX::FetchRequest.new
|
203
|
+
|
204
|
+
# look through the :required and :optional fields for URIs (AX identifiers)
|
205
|
+
fields[:required].each do |f|
|
206
|
+
next unless f =~ /^https?:\/\//
|
207
|
+
ax_request.add( OpenID::AX::AttrInfo.new( f, nil, true ) )
|
208
|
+
end
|
209
|
+
|
210
|
+
fields[:optional].each do |f|
|
211
|
+
next unless f =~ /^https?:\/\//
|
212
|
+
ax_request.add( OpenID::AX::AttrInfo.new( f, nil, false ) )
|
213
|
+
end
|
214
|
+
|
215
|
+
open_id_request.add_extension( ax_request )
|
216
|
+
end
|
217
|
+
|
218
|
+
def open_id_redirect_url(open_id_request, return_to = nil, method = nil)
|
219
|
+
open_id_request.return_to_args['_method'] = (method || request.method).to_s
|
220
|
+
open_id_request.return_to_args['open_id_complete'] = '1'
|
221
|
+
open_id_request.redirect_url(root_url, return_to || requested_url)
|
222
|
+
end
|
223
|
+
|
224
|
+
def requested_url
|
225
|
+
relative_url_root = self.class.respond_to?(:relative_url_root) ?
|
226
|
+
self.class.relative_url_root.to_s :
|
227
|
+
request.relative_url_root
|
228
|
+
"#{request.protocol}#{request.host_with_port}#{ActionController::Base.relative_url_root}#{request.path}"
|
229
|
+
end
|
230
|
+
|
231
|
+
def timeout_protection_from_identity_server
|
232
|
+
yield
|
233
|
+
rescue Timeout::Error
|
234
|
+
Class.new do
|
235
|
+
def status
|
236
|
+
OpenID::FAILURE
|
237
|
+
end
|
238
|
+
|
239
|
+
def msg
|
240
|
+
"Identity server timed out"
|
241
|
+
end
|
242
|
+
end.new
|
243
|
+
end
|
244
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
namespace :open_id_authentication do
|
2
|
+
namespace :db do
|
3
|
+
desc "Creates authentication tables for use with OpenIdAuthentication"
|
4
|
+
task :create => :environment do
|
5
|
+
generate_migration(["open_id_authentication_tables", "add_open_id_authentication_tables"])
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "Upgrade authentication tables from ruby-openid 1.x.x to 2.x.x"
|
9
|
+
task :upgrade => :environment do
|
10
|
+
generate_migration(["upgrade_open_id_authentication_tables", "upgrade_open_id_authentication_tables"])
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate_migration(args)
|
14
|
+
require 'rails_generator'
|
15
|
+
require 'rails_generator/scripts/generate'
|
16
|
+
|
17
|
+
if ActiveRecord::Base.connection.supports_migrations?
|
18
|
+
Rails::Generator::Scripts::Generate.new.run(args)
|
19
|
+
else
|
20
|
+
raise "Task unavailable to this database (no migration support)"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Clear the authentication tables"
|
25
|
+
task :clear => :environment do
|
26
|
+
OpenIdAuthentication::DbStore.cleanup_nonces
|
27
|
+
OpenIdAuthentication::DbStore.cleanup_associations
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
require File.dirname(__FILE__) + '/../lib/open_id_authentication/mem_cache_store'
|
3
|
+
|
4
|
+
# Mock MemCacheStore with MemoryStore for testing
|
5
|
+
class OpenIdAuthentication::MemCacheStore < OpenID::Store::Interface
|
6
|
+
def initialize(*addresses)
|
7
|
+
@connection = ActiveSupport::Cache::MemoryStore.new
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class MemCacheStoreTest < Test::Unit::TestCase
|
12
|
+
ALLOWED_HANDLE = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
|
13
|
+
|
14
|
+
def setup
|
15
|
+
@store = OpenIdAuthentication::MemCacheStore.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_store
|
19
|
+
server_url = "http://www.myopenid.com/openid"
|
20
|
+
assoc = gen_assoc(0)
|
21
|
+
|
22
|
+
# Make sure that a missing association returns no result
|
23
|
+
assert_retrieve(server_url)
|
24
|
+
|
25
|
+
# Check that after storage, getting returns the same result
|
26
|
+
@store.store_association(server_url, assoc)
|
27
|
+
assert_retrieve(server_url, nil, assoc)
|
28
|
+
|
29
|
+
# more than once
|
30
|
+
assert_retrieve(server_url, nil, assoc)
|
31
|
+
|
32
|
+
# Storing more than once has no ill effect
|
33
|
+
@store.store_association(server_url, assoc)
|
34
|
+
assert_retrieve(server_url, nil, assoc)
|
35
|
+
|
36
|
+
# Removing an association that does not exist returns not present
|
37
|
+
assert_remove(server_url, assoc.handle + 'x', false)
|
38
|
+
|
39
|
+
# Removing an association that does not exist returns not present
|
40
|
+
assert_remove(server_url + 'x', assoc.handle, false)
|
41
|
+
|
42
|
+
# Removing an association that is present returns present
|
43
|
+
assert_remove(server_url, assoc.handle, true)
|
44
|
+
|
45
|
+
# but not present on subsequent calls
|
46
|
+
assert_remove(server_url, assoc.handle, false)
|
47
|
+
|
48
|
+
# Put assoc back in the store
|
49
|
+
@store.store_association(server_url, assoc)
|
50
|
+
|
51
|
+
# More recent and expires after assoc
|
52
|
+
assoc2 = gen_assoc(1)
|
53
|
+
@store.store_association(server_url, assoc2)
|
54
|
+
|
55
|
+
# After storing an association with a different handle, but the
|
56
|
+
# same server_url, the handle with the later expiration is returned.
|
57
|
+
assert_retrieve(server_url, nil, assoc2)
|
58
|
+
|
59
|
+
# We can still retrieve the older association
|
60
|
+
assert_retrieve(server_url, assoc.handle, assoc)
|
61
|
+
|
62
|
+
# Plus we can retrieve the association with the later expiration
|
63
|
+
# explicitly
|
64
|
+
assert_retrieve(server_url, assoc2.handle, assoc2)
|
65
|
+
|
66
|
+
# More recent, and expires earlier than assoc2 or assoc. Make sure
|
67
|
+
# that we're picking the one with the latest issued date and not
|
68
|
+
# taking into account the expiration.
|
69
|
+
assoc3 = gen_assoc(2, 100)
|
70
|
+
@store.store_association(server_url, assoc3)
|
71
|
+
|
72
|
+
assert_retrieve(server_url, nil, assoc3)
|
73
|
+
assert_retrieve(server_url, assoc.handle, assoc)
|
74
|
+
assert_retrieve(server_url, assoc2.handle, assoc2)
|
75
|
+
assert_retrieve(server_url, assoc3.handle, assoc3)
|
76
|
+
|
77
|
+
assert_remove(server_url, assoc2.handle, true)
|
78
|
+
|
79
|
+
assert_retrieve(server_url, nil, assoc3)
|
80
|
+
assert_retrieve(server_url, assoc.handle, assoc)
|
81
|
+
assert_retrieve(server_url, assoc2.handle, nil)
|
82
|
+
assert_retrieve(server_url, assoc3.handle, assoc3)
|
83
|
+
|
84
|
+
assert_remove(server_url, assoc2.handle, false)
|
85
|
+
assert_remove(server_url, assoc3.handle, true)
|
86
|
+
|
87
|
+
assert_retrieve(server_url, nil, assoc)
|
88
|
+
assert_retrieve(server_url, assoc.handle, assoc)
|
89
|
+
assert_retrieve(server_url, assoc2.handle, nil)
|
90
|
+
assert_retrieve(server_url, assoc3.handle, nil)
|
91
|
+
|
92
|
+
assert_remove(server_url, assoc2.handle, false)
|
93
|
+
assert_remove(server_url, assoc.handle, true)
|
94
|
+
assert_remove(server_url, assoc3.handle, false)
|
95
|
+
|
96
|
+
assert_retrieve(server_url, nil, nil)
|
97
|
+
assert_retrieve(server_url, assoc.handle, nil)
|
98
|
+
assert_retrieve(server_url, assoc2.handle, nil)
|
99
|
+
assert_retrieve(server_url, assoc3.handle, nil)
|
100
|
+
|
101
|
+
assert_remove(server_url, assoc2.handle, false)
|
102
|
+
assert_remove(server_url, assoc.handle, false)
|
103
|
+
assert_remove(server_url, assoc3.handle, false)
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_nonce
|
107
|
+
server_url = "http://www.myopenid.com/openid"
|
108
|
+
|
109
|
+
[server_url, ''].each do |url|
|
110
|
+
nonce1 = OpenID::Nonce::mk_nonce
|
111
|
+
|
112
|
+
assert_nonce(nonce1, true, url, "#{url}: nonce allowed by default")
|
113
|
+
assert_nonce(nonce1, false, url, "#{url}: nonce not allowed twice")
|
114
|
+
assert_nonce(nonce1, false, url, "#{url}: nonce not allowed third time")
|
115
|
+
|
116
|
+
# old nonces shouldn't pass
|
117
|
+
old_nonce = OpenID::Nonce::mk_nonce(3600)
|
118
|
+
assert_nonce(old_nonce, false, url, "Old nonce #{old_nonce.inspect} passed")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
def gen_assoc(issued, lifetime = 600)
|
124
|
+
secret = OpenID::CryptUtil.random_string(20, nil)
|
125
|
+
handle = OpenID::CryptUtil.random_string(128, ALLOWED_HANDLE)
|
126
|
+
OpenID::Association.new(handle, secret, Time.now + issued, lifetime, 'HMAC-SHA1')
|
127
|
+
end
|
128
|
+
|
129
|
+
def assert_retrieve(url, handle = nil, expected = nil)
|
130
|
+
assoc = @store.get_association(url, handle)
|
131
|
+
|
132
|
+
if expected.nil?
|
133
|
+
assert_nil(assoc)
|
134
|
+
else
|
135
|
+
assert_equal(expected, assoc)
|
136
|
+
assert_equal(expected.handle, assoc.handle)
|
137
|
+
assert_equal(expected.secret, assoc.secret)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def assert_remove(url, handle, expected)
|
142
|
+
present = @store.remove_association(url, handle)
|
143
|
+
assert_equal(expected, present)
|
144
|
+
end
|
145
|
+
|
146
|
+
def assert_nonce(nonce, expected, server_url, msg = "")
|
147
|
+
stamp, salt = OpenID::Nonce::split_nonce(nonce)
|
148
|
+
actual = @store.use_nonce(server_url, stamp, salt)
|
149
|
+
assert_equal(expected, actual, msg)
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class NormalizeTest < Test::Unit::TestCase
|
4
|
+
include OpenIdAuthentication
|
5
|
+
|
6
|
+
NORMALIZATIONS = {
|
7
|
+
"openid.aol.com/nextangler" => "http://openid.aol.com/nextangler",
|
8
|
+
"http://openid.aol.com/nextangler" => "http://openid.aol.com/nextangler",
|
9
|
+
"https://openid.aol.com/nextangler" => "https://openid.aol.com/nextangler",
|
10
|
+
"HTTP://OPENID.AOL.COM/NEXTANGLER" => "http://openid.aol.com/NEXTANGLER",
|
11
|
+
"HTTPS://OPENID.AOL.COM/NEXTANGLER" => "https://openid.aol.com/NEXTANGLER",
|
12
|
+
"loudthinking.com" => "http://loudthinking.com/",
|
13
|
+
"http://loudthinking.com" => "http://loudthinking.com/",
|
14
|
+
"http://loudthinking.com:80" => "http://loudthinking.com/",
|
15
|
+
"https://loudthinking.com:443" => "https://loudthinking.com/",
|
16
|
+
"http://loudthinking.com:8080" => "http://loudthinking.com:8080/",
|
17
|
+
"techno-weenie.net" => "http://techno-weenie.net/",
|
18
|
+
"http://techno-weenie.net" => "http://techno-weenie.net/",
|
19
|
+
"http://techno-weenie.net " => "http://techno-weenie.net/",
|
20
|
+
"=name" => "=name"
|
21
|
+
}
|
22
|
+
|
23
|
+
def test_normalizations
|
24
|
+
NORMALIZATIONS.each do |from, to|
|
25
|
+
assert_equal to, normalize_identifier(from)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_broken_open_id
|
30
|
+
assert_raises(InvalidOpenId) { normalize_identifier(nil) }
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class OpenIdAuthenticationTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@controller = Class.new do
|
6
|
+
include OpenIdAuthentication
|
7
|
+
def params() {} end
|
8
|
+
end.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_authentication_should_fail_when_the_identity_server_is_missing
|
12
|
+
open_id_consumer = mock()
|
13
|
+
open_id_consumer.expects(:begin).raises(OpenID::OpenIDError)
|
14
|
+
@controller.expects(:open_id_consumer).returns(open_id_consumer)
|
15
|
+
@controller.expects(:logger).returns(mock(:error => true))
|
16
|
+
|
17
|
+
@controller.send(:authenticate_with_open_id, "http://someone.example.com") do |result, identity_url|
|
18
|
+
assert result.missing?
|
19
|
+
assert_equal "Sorry, the OpenID server couldn't be found", result.message
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_authentication_should_be_invalid_when_the_identity_url_is_invalid
|
24
|
+
@controller.send(:authenticate_with_open_id, "!") do |result, identity_url|
|
25
|
+
assert result.invalid?, "Result expected to be invalid but was not"
|
26
|
+
assert_equal "Sorry, but this does not appear to be a valid OpenID", result.message
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_authentication_should_fail_when_the_identity_server_times_out
|
31
|
+
open_id_consumer = mock()
|
32
|
+
open_id_consumer.expects(:begin).raises(Timeout::Error, "Identity Server took too long.")
|
33
|
+
@controller.expects(:open_id_consumer).returns(open_id_consumer)
|
34
|
+
@controller.expects(:logger).returns(mock(:error => true))
|
35
|
+
|
36
|
+
@controller.send(:authenticate_with_open_id, "http://someone.example.com") do |result, identity_url|
|
37
|
+
assert result.missing?
|
38
|
+
assert_equal "Sorry, the OpenID server couldn't be found", result.message
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_authentication_should_begin_when_the_identity_server_is_present
|
43
|
+
@controller.expects(:begin_open_id_authentication)
|
44
|
+
@controller.send(:authenticate_with_open_id, "http://someone.example.com")
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class StatusTest < Test::Unit::TestCase
|
4
|
+
include OpenIdAuthentication
|
5
|
+
|
6
|
+
def test_state_conditional
|
7
|
+
assert Result[:missing].missing?
|
8
|
+
assert Result[:missing].unsuccessful?
|
9
|
+
assert !Result[:missing].successful?
|
10
|
+
|
11
|
+
assert Result[:successful].successful?
|
12
|
+
assert !Result[:successful].unsuccessful?
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
gem 'activesupport'
|
5
|
+
require 'active_support'
|
6
|
+
|
7
|
+
gem 'actionpack'
|
8
|
+
require 'action_controller'
|
9
|
+
|
10
|
+
gem 'mocha'
|
11
|
+
require 'mocha'
|
12
|
+
|
13
|
+
gem 'ruby-openid'
|
14
|
+
require 'openid'
|
15
|
+
|
16
|
+
RAILS_ROOT = File.dirname(__FILE__) unless defined? RAILS_ROOT
|
17
|
+
require File.dirname(__FILE__) + "/../lib/open_id_authentication"
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# The only reason I am doing all of this non sense is becuase the openid_authentication requires that
|
2
|
+
# these constants be present. The only other alternative is to use an entire rails application for testing
|
3
|
+
# which is a little too overboard for this, I think.
|
4
|
+
|
5
|
+
RAILS_ROOT = ''
|
6
|
+
|
7
|
+
class ActionController < Authlogic::TestCase::MockController
|
8
|
+
class Request < Authlogic::TestCase::MockRequest
|
9
|
+
def request_method
|
10
|
+
""
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def root_url
|
15
|
+
''
|
16
|
+
end
|
17
|
+
|
18
|
+
def request
|
19
|
+
return @request if defined?(@request)
|
20
|
+
super
|
21
|
+
# Rails does some crazy s#!t with the "method" method. If I don't do this I get a "wrong arguments (0 for 1) error"
|
22
|
+
@request.class.class_eval do
|
23
|
+
def method
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
@request
|
28
|
+
end
|
29
|
+
|
30
|
+
def url_for(*args)
|
31
|
+
''
|
32
|
+
end
|
33
|
+
|
34
|
+
def redirecting_to
|
35
|
+
@redirect_to
|
36
|
+
end
|
37
|
+
|
38
|
+
def redirect_to(*args)
|
39
|
+
@redirect_to = args
|
40
|
+
end
|
41
|
+
end
|
data/test/libs/user.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class SessionTest < ActiveSupport::TestCase
|
4
|
+
def test_openid_identifier
|
5
|
+
session = UserSession.new
|
6
|
+
assert session.respond_to?(:openid_identifier)
|
7
|
+
session.openid_identifier = "test"
|
8
|
+
assert_equal "http://test/", session.openid_identifier
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_validate_openid_error
|
12
|
+
session = UserSession.new
|
13
|
+
session.openid_identifier = "yes"
|
14
|
+
session.openid_identifier = "%"
|
15
|
+
assert_nil session.openid_identifier
|
16
|
+
assert !session.save
|
17
|
+
assert session.errors.on(:openid_identifier)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_validate_by_nil_openid_identifier
|
21
|
+
session = UserSession.new
|
22
|
+
assert !session.save
|
23
|
+
assert !redirecting_to_yahoo?
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_validate_by_correct_openid_identifier
|
27
|
+
session = UserSession.new
|
28
|
+
session.openid_identifier = "https://me.yahoo.com/a/9W0FJjRj0o981TMSs0vqVxPdmMUVOQ--"
|
29
|
+
assert !session.save
|
30
|
+
assert redirecting_to_yahoo?
|
31
|
+
end
|
32
|
+
end
|