nov-ruby-openid 2.1.9

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.
Files changed (203) hide show
  1. data/CHANGELOG +215 -0
  2. data/CHANGES-2.1.0 +36 -0
  3. data/INSTALL +47 -0
  4. data/LICENSE +210 -0
  5. data/NOTICE +2 -0
  6. data/README +81 -0
  7. data/Rakefile +98 -0
  8. data/UPGRADE +127 -0
  9. data/VERSION +1 -0
  10. data/contrib/google/ruby-openid-apps-discovery-1.0.gem +0 -0
  11. data/contrib/google/ruby-openid-apps-discovery-1.01.gem +0 -0
  12. data/examples/README +32 -0
  13. data/examples/active_record_openid_store/README +58 -0
  14. data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +24 -0
  15. data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
  16. data/examples/active_record_openid_store/init.rb +8 -0
  17. data/examples/active_record_openid_store/lib/association.rb +10 -0
  18. data/examples/active_record_openid_store/lib/nonce.rb +3 -0
  19. data/examples/active_record_openid_store/lib/open_id_setting.rb +4 -0
  20. data/examples/active_record_openid_store/lib/openid_ar_store.rb +57 -0
  21. data/examples/active_record_openid_store/test/store_test.rb +212 -0
  22. data/examples/discover +49 -0
  23. data/examples/rails_openid/README +153 -0
  24. data/examples/rails_openid/Rakefile +10 -0
  25. data/examples/rails_openid/app/controllers/application.rb +4 -0
  26. data/examples/rails_openid/app/controllers/consumer_controller.rb +122 -0
  27. data/examples/rails_openid/app/controllers/login_controller.rb +45 -0
  28. data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
  29. data/examples/rails_openid/app/helpers/application_helper.rb +3 -0
  30. data/examples/rails_openid/app/helpers/login_helper.rb +2 -0
  31. data/examples/rails_openid/app/helpers/server_helper.rb +9 -0
  32. data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
  33. data/examples/rails_openid/app/views/layouts/server.rhtml +68 -0
  34. data/examples/rails_openid/app/views/login/index.rhtml +56 -0
  35. data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
  36. data/examples/rails_openid/config/boot.rb +19 -0
  37. data/examples/rails_openid/config/database.yml +74 -0
  38. data/examples/rails_openid/config/environment.rb +54 -0
  39. data/examples/rails_openid/config/environments/development.rb +19 -0
  40. data/examples/rails_openid/config/environments/production.rb +19 -0
  41. data/examples/rails_openid/config/environments/test.rb +19 -0
  42. data/examples/rails_openid/config/routes.rb +24 -0
  43. data/examples/rails_openid/doc/README_FOR_APP +2 -0
  44. data/examples/rails_openid/public/.htaccess +40 -0
  45. data/examples/rails_openid/public/404.html +8 -0
  46. data/examples/rails_openid/public/500.html +8 -0
  47. data/examples/rails_openid/public/dispatch.cgi +12 -0
  48. data/examples/rails_openid/public/dispatch.fcgi +26 -0
  49. data/examples/rails_openid/public/dispatch.rb +12 -0
  50. data/examples/rails_openid/public/favicon.ico +0 -0
  51. data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
  52. data/examples/rails_openid/public/javascripts/controls.js +750 -0
  53. data/examples/rails_openid/public/javascripts/dragdrop.js +584 -0
  54. data/examples/rails_openid/public/javascripts/effects.js +854 -0
  55. data/examples/rails_openid/public/javascripts/prototype.js +1785 -0
  56. data/examples/rails_openid/public/robots.txt +1 -0
  57. data/examples/rails_openid/script/about +3 -0
  58. data/examples/rails_openid/script/breakpointer +3 -0
  59. data/examples/rails_openid/script/console +3 -0
  60. data/examples/rails_openid/script/destroy +3 -0
  61. data/examples/rails_openid/script/generate +3 -0
  62. data/examples/rails_openid/script/performance/benchmarker +3 -0
  63. data/examples/rails_openid/script/performance/profiler +3 -0
  64. data/examples/rails_openid/script/plugin +3 -0
  65. data/examples/rails_openid/script/process/reaper +3 -0
  66. data/examples/rails_openid/script/process/spawner +3 -0
  67. data/examples/rails_openid/script/process/spinner +3 -0
  68. data/examples/rails_openid/script/runner +3 -0
  69. data/examples/rails_openid/script/server +3 -0
  70. data/examples/rails_openid/test/functional/login_controller_test.rb +18 -0
  71. data/examples/rails_openid/test/functional/server_controller_test.rb +18 -0
  72. data/examples/rails_openid/test/test_helper.rb +28 -0
  73. data/lib/hmac/hmac.rb +112 -0
  74. data/lib/hmac/sha1.rb +11 -0
  75. data/lib/hmac/sha2.rb +25 -0
  76. data/lib/openid.rb +20 -0
  77. data/lib/openid/association.rb +249 -0
  78. data/lib/openid/consumer.rb +395 -0
  79. data/lib/openid/consumer/associationmanager.rb +344 -0
  80. data/lib/openid/consumer/checkid_request.rb +186 -0
  81. data/lib/openid/consumer/discovery.rb +497 -0
  82. data/lib/openid/consumer/discovery_manager.rb +123 -0
  83. data/lib/openid/consumer/html_parse.rb +134 -0
  84. data/lib/openid/consumer/idres.rb +523 -0
  85. data/lib/openid/consumer/responses.rb +148 -0
  86. data/lib/openid/cryptutil.rb +115 -0
  87. data/lib/openid/dh.rb +89 -0
  88. data/lib/openid/extension.rb +39 -0
  89. data/lib/openid/extensions/ax.rb +539 -0
  90. data/lib/openid/extensions/oauth.rb +91 -0
  91. data/lib/openid/extensions/pape.rb +179 -0
  92. data/lib/openid/extensions/sreg.rb +277 -0
  93. data/lib/openid/extensions/ui.rb +53 -0
  94. data/lib/openid/extras.rb +11 -0
  95. data/lib/openid/fetchers.rb +258 -0
  96. data/lib/openid/kvform.rb +136 -0
  97. data/lib/openid/kvpost.rb +58 -0
  98. data/lib/openid/message.rb +553 -0
  99. data/lib/openid/protocolerror.rb +8 -0
  100. data/lib/openid/server.rb +1544 -0
  101. data/lib/openid/store/filesystem.rb +271 -0
  102. data/lib/openid/store/interface.rb +75 -0
  103. data/lib/openid/store/memcache.rb +107 -0
  104. data/lib/openid/store/memory.rb +84 -0
  105. data/lib/openid/store/nonce.rb +68 -0
  106. data/lib/openid/trustroot.rb +349 -0
  107. data/lib/openid/urinorm.rb +75 -0
  108. data/lib/openid/util.rb +110 -0
  109. data/lib/openid/yadis/accept.rb +148 -0
  110. data/lib/openid/yadis/constants.rb +21 -0
  111. data/lib/openid/yadis/discovery.rb +153 -0
  112. data/lib/openid/yadis/filters.rb +205 -0
  113. data/lib/openid/yadis/htmltokenizer.rb +305 -0
  114. data/lib/openid/yadis/parsehtml.rb +45 -0
  115. data/lib/openid/yadis/services.rb +42 -0
  116. data/lib/openid/yadis/xrds.rb +155 -0
  117. data/lib/openid/yadis/xri.rb +90 -0
  118. data/lib/openid/yadis/xrires.rb +99 -0
  119. data/setup.rb +1551 -0
  120. data/test/data/accept.txt +124 -0
  121. data/test/data/dh.txt +29 -0
  122. data/test/data/example-xrds.xml +14 -0
  123. data/test/data/linkparse.txt +587 -0
  124. data/test/data/n2b64 +650 -0
  125. data/test/data/test1-discover.txt +137 -0
  126. data/test/data/test1-parsehtml.txt +152 -0
  127. data/test/data/test_discover/malformed_meta_tag.html +19 -0
  128. data/test/data/test_discover/openid.html +11 -0
  129. data/test/data/test_discover/openid2.html +11 -0
  130. data/test/data/test_discover/openid2_xrds.xml +12 -0
  131. data/test/data/test_discover/openid2_xrds_no_local_id.xml +11 -0
  132. data/test/data/test_discover/openid_1_and_2.html +11 -0
  133. data/test/data/test_discover/openid_1_and_2_xrds.xml +16 -0
  134. data/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
  135. data/test/data/test_discover/openid_and_yadis.html +12 -0
  136. data/test/data/test_discover/openid_no_delegate.html +10 -0
  137. data/test/data/test_discover/openid_utf8.html +11 -0
  138. data/test/data/test_discover/yadis_0entries.xml +12 -0
  139. data/test/data/test_discover/yadis_2_bad_local_id.xml +15 -0
  140. data/test/data/test_discover/yadis_2entries_delegate.xml +22 -0
  141. data/test/data/test_discover/yadis_2entries_idp.xml +21 -0
  142. data/test/data/test_discover/yadis_another_delegate.xml +14 -0
  143. data/test/data/test_discover/yadis_idp.xml +12 -0
  144. data/test/data/test_discover/yadis_idp_delegate.xml +13 -0
  145. data/test/data/test_discover/yadis_no_delegate.xml +11 -0
  146. data/test/data/test_xrds/=j3h.2007.11.14.xrds +25 -0
  147. data/test/data/test_xrds/README +12 -0
  148. data/test/data/test_xrds/delegated-20060809-r1.xrds +34 -0
  149. data/test/data/test_xrds/delegated-20060809-r2.xrds +34 -0
  150. data/test/data/test_xrds/delegated-20060809.xrds +34 -0
  151. data/test/data/test_xrds/no-xrd.xml +7 -0
  152. data/test/data/test_xrds/not-xrds.xml +2 -0
  153. data/test/data/test_xrds/prefixsometimes.xrds +34 -0
  154. data/test/data/test_xrds/ref.xrds +109 -0
  155. data/test/data/test_xrds/sometimesprefix.xrds +34 -0
  156. data/test/data/test_xrds/spoof1.xrds +25 -0
  157. data/test/data/test_xrds/spoof2.xrds +25 -0
  158. data/test/data/test_xrds/spoof3.xrds +37 -0
  159. data/test/data/test_xrds/status222.xrds +9 -0
  160. data/test/data/test_xrds/subsegments.xrds +58 -0
  161. data/test/data/test_xrds/valid-populated-xrds.xml +39 -0
  162. data/test/data/trustroot.txt +153 -0
  163. data/test/data/urinorm.txt +79 -0
  164. data/test/discoverdata.rb +131 -0
  165. data/test/test_accept.rb +170 -0
  166. data/test/test_association.rb +266 -0
  167. data/test/test_associationmanager.rb +917 -0
  168. data/test/test_ax.rb +690 -0
  169. data/test/test_checkid_request.rb +294 -0
  170. data/test/test_consumer.rb +257 -0
  171. data/test/test_cryptutil.rb +119 -0
  172. data/test/test_dh.rb +86 -0
  173. data/test/test_discover.rb +852 -0
  174. data/test/test_discovery_manager.rb +262 -0
  175. data/test/test_extension.rb +46 -0
  176. data/test/test_extras.rb +35 -0
  177. data/test/test_fetchers.rb +565 -0
  178. data/test/test_filters.rb +270 -0
  179. data/test/test_idres.rb +963 -0
  180. data/test/test_kvform.rb +165 -0
  181. data/test/test_kvpost.rb +65 -0
  182. data/test/test_linkparse.rb +101 -0
  183. data/test/test_message.rb +1116 -0
  184. data/test/test_nonce.rb +89 -0
  185. data/test/test_oauth.rb +175 -0
  186. data/test/test_openid_yadis.rb +178 -0
  187. data/test/test_pape.rb +247 -0
  188. data/test/test_parsehtml.rb +80 -0
  189. data/test/test_responses.rb +63 -0
  190. data/test/test_server.rb +2457 -0
  191. data/test/test_sreg.rb +479 -0
  192. data/test/test_stores.rb +298 -0
  193. data/test/test_trustroot.rb +113 -0
  194. data/test/test_ui.rb +93 -0
  195. data/test/test_urinorm.rb +35 -0
  196. data/test/test_util.rb +145 -0
  197. data/test/test_xrds.rb +169 -0
  198. data/test/test_xri.rb +48 -0
  199. data/test/test_xrires.rb +63 -0
  200. data/test/test_yadis_discovery.rb +220 -0
  201. data/test/testutil.rb +127 -0
  202. data/test/util.rb +53 -0
  203. metadata +336 -0
@@ -0,0 +1,262 @@
1
+
2
+ require 'test/unit'
3
+ require 'openid/consumer/discovery_manager'
4
+ require 'openid/extras'
5
+
6
+ require 'testutil'
7
+
8
+ module OpenID
9
+ class TestDiscoveredServices < Test::Unit::TestCase
10
+ def setup
11
+ @starting_url = "http://starting.url.com/"
12
+ @yadis_url = "http://starting.url.com/xrds"
13
+ @services = ["bogus", "not_a_service"]
14
+
15
+ @disco_services = Consumer::DiscoveredServices.new(@starting_url,
16
+ @yadis_url,
17
+ @services.dup)
18
+ end
19
+
20
+ def test_next
21
+ assert_equal(@disco_services.next, @services[0])
22
+ assert_equal(@disco_services.current, @services[0])
23
+
24
+ assert_equal(@disco_services.next, @services[1])
25
+ assert_equal(@disco_services.current, @services[1])
26
+
27
+ assert_equal(@disco_services.next, nil)
28
+ assert_equal(@disco_services.current, nil)
29
+ end
30
+
31
+ def test_for_url
32
+ assert(@disco_services.for_url?(@starting_url))
33
+ assert(@disco_services.for_url?(@yadis_url))
34
+
35
+ assert(!@disco_services.for_url?(nil))
36
+ assert(!@disco_services.for_url?("invalid"))
37
+ end
38
+
39
+ def test_started
40
+ assert(!@disco_services.started?)
41
+ @disco_services.next
42
+ assert(@disco_services.started?)
43
+ @disco_services.next
44
+ assert(@disco_services.started?)
45
+ @disco_services.next
46
+ assert(!@disco_services.started?)
47
+ end
48
+
49
+ def test_empty
50
+ assert(Consumer::DiscoveredServices.new(nil, nil, []).empty?)
51
+
52
+ assert(!@disco_services.empty?)
53
+
54
+ @disco_services.next
55
+ @disco_services.next
56
+
57
+ assert(@disco_services.started?)
58
+ end
59
+ end
60
+
61
+ # I need to be able to test the protected methods; this lets me do
62
+ # that.
63
+ class PassthroughDiscoveryManager < Consumer::DiscoveryManager
64
+ def method_missing(m, *args)
65
+ method(m).call(*args)
66
+ end
67
+ end
68
+
69
+ class TestDiscoveryManager < Test::Unit::TestCase
70
+ def setup
71
+ @session = {}
72
+ @url = "http://unittest.com/"
73
+ @key_suffix = "testing"
74
+ @yadis_url = "http://unittest.com/xrds"
75
+ @manager = PassthroughDiscoveryManager.new(@session, @url, @key_suffix)
76
+ @key = @manager.session_key
77
+ end
78
+
79
+ def test_construct
80
+ # Make sure the default session key suffix is not nil.
81
+ m = Consumer::DiscoveryManager.new(nil, nil)
82
+ assert(!m.instance_variable_get("@session_key_suffix").nil?)
83
+
84
+ m = Consumer::DiscoveryManager.new(nil, nil, "override")
85
+ assert_equal(m.instance_variable_get("@session_key_suffix"), "override")
86
+ end
87
+
88
+ def test_get_next_service
89
+ assert_equal(@session[@key], nil)
90
+
91
+ next_service = @manager.get_next_service {
92
+ [@yadis_url, ["one", "two", "three"]]
93
+ }
94
+
95
+ disco = @session[@key]
96
+ assert_equal(disco.current, "one")
97
+ assert_equal(next_service, "one")
98
+ assert(disco.for_url?(@url))
99
+ assert(disco.for_url?(@yadis_url))
100
+
101
+ # The first two calls to get_next_service should return the
102
+ # services in @disco.
103
+ assert_equal(@manager.get_next_service, "two")
104
+ assert_equal(@manager.get_next_service, "three")
105
+ assert_equal(@session[@key], disco)
106
+
107
+ # The manager is exhausted and should be deleted and a new one
108
+ # should be created.
109
+ @manager.get_next_service {
110
+ [@yadis_url, ["four"]]
111
+ }
112
+
113
+ disco2 = @session[@key]
114
+ assert_equal(disco2.current, "four")
115
+
116
+ # create_manager may return a nil manager, in which case the
117
+ # next service should be nil.
118
+ @manager.extend(OpenID::InstanceDefExtension)
119
+ @manager.instance_def(:create_manager) do |yadis_url, services|
120
+ nil
121
+ end
122
+
123
+ result = @manager.get_next_service { |url|
124
+ ["unused", []]
125
+ }
126
+
127
+ assert_equal(result, nil)
128
+ end
129
+
130
+ def test_cleanup
131
+ # With no preexisting manager, cleanup() returns nil.
132
+ assert_equal(@manager.cleanup, nil)
133
+
134
+ # With a manager, it returns the manager's current service.
135
+ disco = Consumer::DiscoveredServices.new(@url, @yadis_url, ["one", "two"])
136
+
137
+ @session[@key] = disco
138
+ assert_equal(@manager.cleanup, nil)
139
+ assert_equal(@session[@key], nil)
140
+
141
+ @session[@key] = disco
142
+ disco.next
143
+ assert_equal(@manager.cleanup, "one")
144
+ assert_equal(@session[@key], nil)
145
+
146
+ # The force parameter should be passed through to get_manager
147
+ # and destroy_manager.
148
+ force_value = "yo"
149
+ testcase = self
150
+
151
+ m = Consumer::DiscoveredServices.new(nil, nil, ["inner"])
152
+ m.next
153
+
154
+ @manager.extend(OpenID::InstanceDefExtension)
155
+ @manager.instance_def(:get_manager) do |force|
156
+ testcase.assert_equal(force, force_value)
157
+ m
158
+ end
159
+
160
+ @manager.instance_def(:destroy_manager) do |force|
161
+ testcase.assert_equal(force, force_value)
162
+ end
163
+
164
+ assert_equal("inner", @manager.cleanup(force_value))
165
+ end
166
+
167
+ def test_get_manager
168
+ # get_manager should always return the loaded manager when
169
+ # forced.
170
+ @session[@key] = "bogus"
171
+ assert_equal("bogus", @manager.get_manager(true))
172
+
173
+ # When not forced, only managers for @url should be returned.
174
+ disco = Consumer::DiscoveredServices.new(@url, @yadis_url, ["one"])
175
+ @session[@key] = disco
176
+ assert_equal(@manager.get_manager, disco)
177
+
178
+ # Try to get_manager for a manger that doesn't manage @url:
179
+ disco2 = Consumer::DiscoveredServices.new("http://not.this.url.com/",
180
+ "http://other.yadis.url/", ["one"])
181
+ @session[@key] = disco2
182
+ assert_equal(@manager.get_manager, nil)
183
+ assert_equal(@manager.get_manager(true), disco2)
184
+ end
185
+
186
+ def test_create_manager
187
+ assert(@session[@key].nil?)
188
+
189
+ services = ["created", "manager"]
190
+ returned_disco = @manager.create_manager(@yadis_url, services)
191
+
192
+ stored_disco = @session[@key]
193
+ assert(stored_disco.for_url?(@yadis_url))
194
+ assert_equal(stored_disco.next, "created")
195
+
196
+ assert_equal(stored_disco, returned_disco)
197
+
198
+ # Calling create_manager with a preexisting manager should
199
+ # result in StandardError.
200
+ assert_raise(StandardError) {
201
+ @manager.create_manager(@yadis_url, services)
202
+ }
203
+
204
+ # create_manager should do nothing (and return nil) if given no
205
+ # services.
206
+ @session[@key] = nil
207
+ result = @manager.create_manager(@yadis_url, [])
208
+ assert(result.nil?)
209
+ assert(@session[@key].nil?)
210
+ end
211
+
212
+ class DestroyCalledException < StandardError; end
213
+
214
+ def test_destroy_manager
215
+ # destroy_manager should remove the manager from the session,
216
+ # forcibly if necessary.
217
+ valid_disco = Consumer::DiscoveredServices.new(@url, @yadis_url, ["serv"])
218
+ invalid_disco = Consumer::DiscoveredServices.new("http://not.mine.com/",
219
+ "http://different.url.com/",
220
+ ["serv"])
221
+
222
+ @session[@key] = valid_disco
223
+ @manager.destroy_manager
224
+ assert(@session[@key].nil?)
225
+
226
+ @session[@key] = invalid_disco
227
+ @manager.destroy_manager
228
+ assert_equal(@session[@key], invalid_disco)
229
+
230
+ # Force destruction of manager, no matter which URLs it's for.
231
+ @manager.destroy_manager(true)
232
+ assert(@session[@key].nil?)
233
+ end
234
+
235
+ def test_session_key
236
+ assert(@manager.session_key.ends_with?(
237
+ @manager.instance_variable_get("@session_key_suffix")))
238
+ end
239
+
240
+ def test_store
241
+ thing = "opaque"
242
+ assert(@session[@key].nil?)
243
+ @manager.store(thing)
244
+ assert_equal(@session[@key], thing)
245
+ end
246
+
247
+ def test_load
248
+ thing = "opaque"
249
+ @session[@key] = thing
250
+ assert_equal(@manager.load, thing)
251
+ end
252
+
253
+ def test_destroy!
254
+ thing = "opaque"
255
+ @manager.store(thing)
256
+ assert_equal(@manager.load, thing)
257
+ @manager.destroy!
258
+ assert(@session[@key].nil?)
259
+ assert(@manager.load.nil?)
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,46 @@
1
+ require 'openid/extension'
2
+ require 'openid/message'
3
+ require 'test/unit'
4
+
5
+ module OpenID
6
+ class DummyExtension < OpenID::Extension
7
+ TEST_URI = 'http://an.extension'
8
+ TEST_ALIAS = 'dummy'
9
+ def initialize
10
+ @ns_uri = TEST_URI
11
+ @ns_alias = TEST_ALIAS
12
+ end
13
+
14
+ def get_extension_args
15
+ return {}
16
+ end
17
+ end
18
+
19
+ class ToMessageTest < Test::Unit::TestCase
20
+ def test_OpenID1
21
+ oid1_msg = Message.new(OPENID1_NS)
22
+ ext = DummyExtension.new
23
+ ext.to_message(oid1_msg)
24
+ namespaces = oid1_msg.namespaces
25
+ assert(namespaces.implicit?(DummyExtension::TEST_URI))
26
+ assert_equal(
27
+ DummyExtension::TEST_URI,
28
+ namespaces.get_namespace_uri(DummyExtension::TEST_ALIAS))
29
+ assert_equal(DummyExtension::TEST_ALIAS,
30
+ namespaces.get_alias(DummyExtension::TEST_URI))
31
+ end
32
+
33
+ def test_OpenID2
34
+ oid2_msg = Message.new(OPENID2_NS)
35
+ ext = DummyExtension.new
36
+ ext.to_message(oid2_msg)
37
+ namespaces = oid2_msg.namespaces
38
+ assert(!namespaces.implicit?(DummyExtension::TEST_URI))
39
+ assert_equal(
40
+ DummyExtension::TEST_URI,
41
+ namespaces.get_namespace_uri(DummyExtension::TEST_ALIAS))
42
+ assert_equal(DummyExtension::TEST_ALIAS,
43
+ namespaces.get_alias(DummyExtension::TEST_URI))
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,35 @@
1
+ require 'test/unit'
2
+ require 'openid/extras'
3
+
4
+ class StartsWithTestCase < Test::Unit::TestCase
5
+ def test_starts_with
6
+ [["anything", ""],
7
+ ["something else", ""],
8
+ ["", ""],
9
+ ["foos", "foo"],
10
+ ].each{|str,target| assert(str.starts_with?(target))}
11
+ end
12
+
13
+ def test_not_starts_with
14
+ [["x", "y"],
15
+ ["foos", "ball"],
16
+ ["xx", "xy"],
17
+ ].each{|str,target| assert(!(str.starts_with? target)) }
18
+ end
19
+
20
+ def test_ends_with
21
+ [["anything", ""],
22
+ ["something else", " else"],
23
+ ["", ""],
24
+ ["foos", "oos"],
25
+ ].each{|str,target| assert(str.ends_with?(target))}
26
+ end
27
+
28
+ def test_not_ends_with
29
+ [["x", "y"],
30
+ ["foos", "ball"],
31
+ ["xx", "xy"],
32
+ ["foosball", "foosbal"],
33
+ ].each{|str,target| assert(!(str.ends_with? target)) }
34
+ end
35
+ end
@@ -0,0 +1,565 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'test/unit'
4
+ require 'net/http'
5
+ require 'webrick'
6
+
7
+ require 'testutil'
8
+ require 'util'
9
+
10
+ require 'openid/fetchers'
11
+
12
+ require 'stringio'
13
+
14
+ begin
15
+ require 'net/https'
16
+ rescue LoadError
17
+ # We need these names for testing.
18
+
19
+ module OpenSSL
20
+ module SSL
21
+ class SSLError < StandardError; end
22
+ end
23
+ end
24
+ end
25
+
26
+ module HttpResultAssertions
27
+ def assert_http_result_is(expected, result)
28
+ assert_equal expected.code, result.code
29
+ assert_equal expected.body, result.body
30
+ assert_equal expected.final_url, result.final_url
31
+ end
32
+ end
33
+
34
+ class BogusFetcher
35
+ RESPONSE = "bogus"
36
+
37
+ def fetch(url, body=nil, headers=nil, redirect_limit=5)
38
+ return BogusFetcher::RESPONSE
39
+ end
40
+ end
41
+
42
+ class FetcherTestCase < Test::Unit::TestCase
43
+ include HttpResultAssertions
44
+ include OpenID::TestUtil
45
+
46
+ @@test_header_name = 'X-test-header'
47
+ @@test_header_value = 'marmoset'
48
+
49
+ class ExpectedResponse < Net::HTTPResponse
50
+ attr_reader :final_url
51
+
52
+ def initialize(code, final_url, body="the expected body",
53
+ httpv="1.1", msg=nil)
54
+ super(httpv, code, msg)
55
+ @code = code
56
+ @body = body
57
+ @final_url = final_url
58
+ end
59
+
60
+ def body
61
+ @body
62
+ end
63
+ end
64
+
65
+ @@cases =
66
+ [
67
+ # path, status code, expected url (nil = default to path)
68
+ ['/success', 200, nil],
69
+ ['/notfound', 404, nil],
70
+ ['/badreq', 400, nil],
71
+ ['/forbidden', 403, nil],
72
+ ['/error', 500, nil],
73
+ ['/server_error', 503, nil],
74
+ ['/301redirect', 200, '/success'],
75
+ ['/302redirect', 200, '/success'],
76
+ ['/303redirect', 200, '/success'],
77
+ ['/307redirect', 200, '/success'],
78
+ ]
79
+
80
+ def _redirect_with_code(code)
81
+ lambda { |req, resp|
82
+ resp.status = code
83
+ resp['Location'] = _uri_build('/success')
84
+ }
85
+ end
86
+
87
+ def _respond_with_code(code)
88
+ lambda { |req, resp|
89
+ resp.status = code
90
+ resp.body = "the expected body"
91
+ }
92
+ end
93
+
94
+ def _require_header
95
+ lambda { |req, resp|
96
+ assert_equal @@test_header_value, req[@@test_header_name]
97
+ assert_match 'ruby-openid', req['User-agent']
98
+ }
99
+ end
100
+
101
+ def _require_post
102
+ lambda { |req, resp|
103
+ assert_equal 'POST', req.request_method
104
+ assert_equal "postbody\n", req.body
105
+ }
106
+ end
107
+
108
+ def _redirect_loop
109
+ lambda { |req, resp|
110
+ @_redirect_counter += 1
111
+ resp.status = 302
112
+ resp['Location'] = _uri_build('/redirect_loop')
113
+ resp.body = "Fetched #{@_redirect_counter} times."
114
+ assert_block("Fetched too many times.") { @_redirect_counter < 10 }
115
+ }
116
+ end
117
+
118
+ UTF8_PAGE_CONTENT = <<-EOHTML
119
+ <html>
120
+ <head><title>UTF-8</title></head>
121
+ <body>こんにちは</body>
122
+ </html>
123
+ EOHTML
124
+ def _utf8_page
125
+ lambda { |req, resp|
126
+ resp['Content-Type'] = "text/html; charset=utf-8"
127
+ body = UTF8_PAGE_CONTENT.dup
128
+ body.force_encoding("ASCII-8BIT") if body.respond_to?(:force_encoding)
129
+ resp.body = body
130
+ }
131
+ end
132
+
133
+ def setup
134
+ @fetcher = OpenID::StandardFetcher.new
135
+ @logfile = StringIO.new
136
+ @weblog = WEBrick::Log.new(logfile=@logfile)
137
+ @server = WEBrick::HTTPServer.new(:Port => 0,
138
+ :Logger => @weblog,
139
+ :AccessLog => [])
140
+ @server_thread = Thread.new {
141
+ @server.mount_proc('/success', _respond_with_code(200))
142
+ @server.mount_proc('/301redirect', _redirect_with_code(301))
143
+ @server.mount_proc('/302redirect', _redirect_with_code(302))
144
+ @server.mount_proc('/303redirect', _redirect_with_code(303))
145
+ @server.mount_proc('/307redirect', _redirect_with_code(307))
146
+ @server.mount_proc('/badreq', _respond_with_code(400))
147
+ @server.mount_proc('/forbidden', _respond_with_code(403))
148
+ @server.mount_proc('/notfound', _respond_with_code(404))
149
+ @server.mount_proc('/error', _respond_with_code(500))
150
+ @server.mount_proc('/server_error', _respond_with_code(503))
151
+ @server.mount_proc('/require_header', _require_header)
152
+ @server.mount_proc('/redirect_to_reqheader') { |req, resp|
153
+ resp.status = 302
154
+ resp['Location'] = _uri_build('/require_header')
155
+ }
156
+ @server.mount_proc('/post', _require_post)
157
+ @server.mount_proc('/redirect_loop', _redirect_loop)
158
+ @server.mount_proc('/utf8_page', _utf8_page)
159
+ @server.start
160
+ }
161
+ @uri = _uri_build
162
+ sleep 0.2
163
+ end
164
+
165
+ def _uri_build(path='/')
166
+ u = URI::HTTP.build({
167
+ :host => @server.config[:ServerName],
168
+ :port => @server.config[:Port],
169
+ :path => path,
170
+ })
171
+ return u.to_s
172
+ end
173
+
174
+ def teardown
175
+ @server.shutdown
176
+ # Sleep a little because sometimes this blocks forever.
177
+ @server_thread.join
178
+ end
179
+
180
+ =begin
181
+ # XXX This test no longer works since we're not dealing with URI
182
+ # objects internally.
183
+ def test_final_url_tainted
184
+ uri = _uri_build('/301redirect')
185
+ result = @fetcher.fetch(uri)
186
+
187
+ final_url = URI::parse(result.final_url)
188
+
189
+ assert final_url.host.tainted?
190
+ assert final_url.path.tainted?
191
+ end
192
+ =end
193
+
194
+ def test_headers
195
+ headers = {
196
+ @@test_header_name => @@test_header_value
197
+ }
198
+ uri = _uri_build('/require_header')
199
+ result = @fetcher.fetch(uri, nil, headers)
200
+ # The real test runs under the WEBrick handler _require_header,
201
+ # this just checks the return code from that.
202
+ assert_equal '200', result.code, @logfile.string
203
+ end
204
+
205
+ def test_headers_after_redirect
206
+ headers = {
207
+ @@test_header_name => @@test_header_value
208
+ }
209
+ uri = _uri_build('/redirect_to_reqheader')
210
+ result = @fetcher.fetch(uri, nil, headers)
211
+ # The real test runs under the WEBrick handler _require_header,
212
+ # this just checks the return code from that.
213
+ assert_equal '200', result.code, @logfile.string
214
+ end
215
+
216
+ def test_post
217
+ uri = _uri_build('/post')
218
+ result = @fetcher.fetch(uri, "postbody\n")
219
+ # The real test runs under the WEBrick handler _require_header,
220
+ # this just checks the return code from that.
221
+ assert_equal '200', result.code, @logfile.string
222
+ end
223
+
224
+ def test_redirect_limit
225
+ @_redirect_counter = 0
226
+ uri = _uri_build('/redirect_loop')
227
+ assert_raise(OpenID::HTTPRedirectLimitReached) {
228
+ @fetcher.fetch(uri)
229
+ }
230
+ end
231
+
232
+ def test_utf8_page
233
+ uri = _uri_build('/utf8_page')
234
+ response = @fetcher.fetch(uri)
235
+ assert_equal(UTF8_PAGE_CONTENT, response.body)
236
+ if response.body.respond_to?(:encoding)
237
+ assert_equal(Encoding::UTF_8, response.body.encoding)
238
+ end
239
+ end
240
+
241
+ def test_cases
242
+ for path, expected_code, expected_url in @@cases
243
+ uri = _uri_build(path)
244
+ if expected_url.nil?
245
+ expected_url = uri
246
+ else
247
+ expected_url = _uri_build(expected_url)
248
+ end
249
+
250
+ expected = ExpectedResponse.new(expected_code.to_s, expected_url)
251
+ result = @fetcher.fetch(uri)
252
+
253
+ begin
254
+ assert_http_result_is expected, result
255
+ rescue Test::Unit::AssertionFailedError => err
256
+ if result.code == '500' && expected_code != 500
257
+ # Looks like our WEBrick harness broke.
258
+ msg = <<EOF
259
+ Status #{result.code} from case #{path}. Logs:
260
+ #{@logfile.string}
261
+ EOF
262
+ raise msg
263
+ end
264
+
265
+ # Wrap failure messages so we can tell which case failed.
266
+ new_msg = "#{path}: #{err.message.to_s}"
267
+ new_err = Test::Unit::AssertionFailedError.new(new_msg)
268
+ new_err.set_backtrace(err.backtrace)
269
+ raise new_err
270
+ end
271
+ end
272
+ end
273
+
274
+ def test_https_no_openssl
275
+ # Override supports_ssl? to always claim that connections don't
276
+ # support SSL. Test the behavior of fetch() for HTTPS URLs in
277
+ # that case.
278
+ f = OpenID::StandardFetcher.new
279
+ f.extend(OpenID::InstanceDefExtension)
280
+
281
+ f.instance_def(:supports_ssl?) do |conn|
282
+ false
283
+ end
284
+
285
+ begin
286
+ f.fetch("https://someurl.com/")
287
+ flunk("Expected RuntimeError")
288
+ rescue RuntimeError => why
289
+ assert_equal(why.to_s, "SSL support not found; cannot fetch https://someurl.com/")
290
+ end
291
+ end
292
+
293
+ class FakeConnection < Net::HTTP
294
+ attr_reader :use_ssl, :ca_file
295
+
296
+ def initialize *args
297
+ super
298
+ @ca_file = nil
299
+ end
300
+
301
+ def use_ssl=(v)
302
+ @use_ssl = v
303
+ end
304
+
305
+ def ca_file=(ca_file)
306
+ @ca_file = ca_file
307
+ end
308
+ end
309
+
310
+ def test_ssl_with_ca_file
311
+ f = OpenID::StandardFetcher.new
312
+ ca_file = "BOGUS"
313
+ f.ca_file = ca_file
314
+
315
+ f.extend(OpenID::InstanceDefExtension)
316
+ f.instance_def(:make_http) do |uri|
317
+ FakeConnection.new(uri.host, uri.port)
318
+ end
319
+
320
+ testcase = self
321
+
322
+ f.instance_def(:set_verified) do |conn, verified|
323
+ testcase.assert(verified)
324
+ end
325
+
326
+ conn = f.make_connection(URI::parse("https://someurl.com"))
327
+ assert_equal(conn.ca_file, ca_file)
328
+ end
329
+
330
+ def test_ssl_without_ca_file
331
+ f = OpenID::StandardFetcher.new
332
+
333
+ f.extend(OpenID::InstanceDefExtension)
334
+ f.instance_def(:make_http) do |uri|
335
+ FakeConnection.new(uri.host, uri.port)
336
+ end
337
+
338
+ testcase = self
339
+
340
+ f.instance_def(:set_verified) do |conn, verified|
341
+ testcase.assert(!verified)
342
+ end
343
+
344
+ conn = nil
345
+ assert_log_matches(/making https request to https:\/\/someurl.com without verifying/) {
346
+ conn = f.make_connection(URI::parse("https://someurl.com"))
347
+ }
348
+
349
+ assert(conn.ca_file.nil?)
350
+ end
351
+
352
+ def test_make_http_nil
353
+ f = OpenID::StandardFetcher.new
354
+
355
+ f.extend(OpenID::InstanceDefExtension)
356
+ f.instance_def(:make_http) do |uri|
357
+ nil
358
+ end
359
+
360
+ assert_raise(RuntimeError) {
361
+ f.make_connection(URI::parse("http://example.com/"))
362
+ }
363
+ end
364
+
365
+ def test_make_http_invalid
366
+ f = OpenID::StandardFetcher.new
367
+
368
+ f.extend(OpenID::InstanceDefExtension)
369
+ f.instance_def(:make_http) do |uri|
370
+ "not a Net::HTTP object"
371
+ end
372
+
373
+ assert_raise(RuntimeError) {
374
+ f.make_connection(URI::parse("http://example.com/"))
375
+ }
376
+ end
377
+
378
+ class BrokenSSLConnection
379
+ def start(&block)
380
+ raise OpenSSL::SSL::SSLError
381
+ end
382
+ end
383
+
384
+ def test_sslfetchingerror
385
+ f = OpenID::StandardFetcher.new
386
+
387
+ f.extend(OpenID::InstanceDefExtension)
388
+ f.instance_def(:make_connection) do |uri|
389
+ BrokenSSLConnection.new
390
+ end
391
+
392
+ assert_raise(OpenID::SSLFetchingError) {
393
+ f.fetch("https://bogus.com/")
394
+ }
395
+ end
396
+
397
+ class TimeoutConnection
398
+ def start(&block)
399
+ raise Timeout::Error
400
+ end
401
+ end
402
+
403
+ def test_fetchingerror
404
+ f = OpenID::StandardFetcher.new
405
+
406
+ f.extend(OpenID::InstanceDefExtension)
407
+ f.instance_def(:make_connection) do |uri|
408
+ TimeoutConnection.new
409
+ end
410
+
411
+ assert_raise(OpenID::FetchingError) {
412
+ f.fetch("https://bogus.com/")
413
+ }
414
+ end
415
+
416
+ class TestingException < OpenID::FetchingError; end
417
+
418
+ class NoSSLSupportConnection
419
+ def supports_ssl?
420
+ false
421
+ end
422
+
423
+ def start
424
+ yield
425
+ end
426
+
427
+ def request_get(*args)
428
+ raise TestingException
429
+ end
430
+
431
+ def post_connection_check(hostname)
432
+ raise RuntimeError
433
+ end
434
+
435
+ def use_ssl?
436
+ true
437
+ end
438
+ end
439
+
440
+ class NoUseSSLConnection < NoSSLSupportConnection
441
+ def use_ssl?
442
+ false
443
+ end
444
+ end
445
+
446
+ def test_post_connection_check_no_support_ssl
447
+ f = OpenID::StandardFetcher.new
448
+
449
+ f.extend(OpenID::InstanceDefExtension)
450
+ f.instance_def(:make_connection) do |uri|
451
+ NoSSLSupportConnection.new
452
+ end
453
+
454
+ # post_connection_check should not be called.
455
+ assert_raise(TestingException) {
456
+ f.fetch("https://bogus.com/")
457
+ }
458
+ end
459
+
460
+ def test_post_connection_check_no_use_ssl
461
+ f = OpenID::StandardFetcher.new
462
+
463
+ f.extend(OpenID::InstanceDefExtension)
464
+ f.instance_def(:make_connection) do |uri|
465
+ NoUseSSLConnection.new
466
+ end
467
+
468
+ # post_connection_check should not be called.
469
+ assert_raise(TestingException) {
470
+ f.fetch("https://bogus.com/")
471
+ }
472
+ end
473
+
474
+ class PostConnectionCheckException < OpenID::FetchingError; end
475
+
476
+ class UseSSLConnection < NoSSLSupportConnection
477
+ def use_ssl?
478
+ true
479
+ end
480
+
481
+ def post_connection_check(hostname)
482
+ raise PostConnectionCheckException
483
+ end
484
+ end
485
+
486
+ def test_post_connection_check
487
+ f = OpenID::StandardFetcher.new
488
+
489
+ f.extend(OpenID::InstanceDefExtension)
490
+ f.instance_def(:make_connection) do |uri|
491
+ UseSSLConnection.new
492
+ end
493
+
494
+ f.instance_def(:supports_ssl?) do |conn|
495
+ true
496
+ end
497
+
498
+ # post_connection_check should be called.
499
+ assert_raise(PostConnectionCheckException) {
500
+ f.fetch("https://bogus.com/")
501
+ }
502
+ end
503
+ end
504
+
505
+ class DefaultFetcherTest < Test::Unit::TestCase
506
+ def setup
507
+ OpenID.fetcher = nil
508
+ end
509
+
510
+ def test_default_fetcher
511
+ assert(OpenID.fetcher.is_a?(OpenID::StandardFetcher))
512
+
513
+ # A custom fetcher can be set
514
+ OpenID.fetcher = BogusFetcher.new
515
+
516
+ # A test fetch should call the new fetcher
517
+ assert(OpenID.fetch('not-a-url') == BogusFetcher::RESPONSE)
518
+
519
+ # Set the fetcher to nil again
520
+ OpenID.fetcher = nil
521
+ assert(OpenID.fetcher.is_a?(OpenID::StandardFetcher))
522
+ end
523
+ end
524
+
525
+ class ProxyTest < Test::Unit::TestCase
526
+ def test_proxy_unreachable
527
+ begin
528
+ f = OpenID::StandardFetcher.new('127.0.0.1', 1)
529
+ # If this tries to connect to the proxy (on port 1), I expect
530
+ # a 'connection refused' error. If it tries to contact the below
531
+ # URI first, it will get some other sort of error.
532
+ f.fetch("http://unittest.invalid")
533
+ rescue OpenID::FetchingError => why
534
+ # XXX: Is this a translatable string that is going to break?
535
+ if why.message =~ /Connection refused/
536
+ return
537
+ end
538
+ raise why
539
+ end
540
+ flunk "expected Connection Refused, but it passed."
541
+ end
542
+
543
+ def test_proxy_env
544
+ ENV['http_proxy'] = 'http://127.0.0.1:3128/'
545
+ OpenID.fetcher_use_env_http_proxy
546
+
547
+ # make_http just to give us something with readable attributes to inspect.
548
+ conn = OpenID.fetcher.make_http(URI.parse('http://127.0.0.2'))
549
+ assert_equal('127.0.0.1', conn.proxy_address)
550
+ assert_equal(3128, conn.proxy_port)
551
+ end
552
+ # These aren't fully automated tests, but if you start a proxy
553
+ # on port 8888 (tinyproxy's default) and check its logs...
554
+ # def test_proxy
555
+ # f = OpenID::StandardFetcher.new('127.0.0.1', 8888)
556
+ # result = f.fetch("http://www.example.com/")
557
+ # assert_match(/RFC.*2606/, result.body)
558
+ # end
559
+
560
+ # def test_proxy_https
561
+ # f = OpenID::StandardFetcher.new('127.0.0.1', 8888)
562
+ # result = f.fetch("https://www.myopenid.com/")
563
+ # assert_match(/myOpenID/, result.body)
564
+ # end
565
+ end