pelle-ruby-openid 2.1.8

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 (197) 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 +82 -0
  7. data/UPGRADE +127 -0
  8. data/VERSION +1 -0
  9. data/examples/README +32 -0
  10. data/examples/active_record_openid_store/README +58 -0
  11. data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +24 -0
  12. data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
  13. data/examples/active_record_openid_store/init.rb +8 -0
  14. data/examples/active_record_openid_store/lib/association.rb +10 -0
  15. data/examples/active_record_openid_store/lib/nonce.rb +3 -0
  16. data/examples/active_record_openid_store/lib/open_id_setting.rb +4 -0
  17. data/examples/active_record_openid_store/lib/openid_ar_store.rb +57 -0
  18. data/examples/active_record_openid_store/test/store_test.rb +212 -0
  19. data/examples/discover +49 -0
  20. data/examples/rails_openid/README +153 -0
  21. data/examples/rails_openid/Rakefile +10 -0
  22. data/examples/rails_openid/app/controllers/application.rb +4 -0
  23. data/examples/rails_openid/app/controllers/consumer_controller.rb +122 -0
  24. data/examples/rails_openid/app/controllers/login_controller.rb +45 -0
  25. data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
  26. data/examples/rails_openid/app/helpers/application_helper.rb +3 -0
  27. data/examples/rails_openid/app/helpers/login_helper.rb +2 -0
  28. data/examples/rails_openid/app/helpers/server_helper.rb +9 -0
  29. data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
  30. data/examples/rails_openid/app/views/layouts/server.rhtml +68 -0
  31. data/examples/rails_openid/app/views/login/index.rhtml +56 -0
  32. data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
  33. data/examples/rails_openid/config/boot.rb +19 -0
  34. data/examples/rails_openid/config/database.yml +74 -0
  35. data/examples/rails_openid/config/environment.rb +54 -0
  36. data/examples/rails_openid/config/environments/development.rb +19 -0
  37. data/examples/rails_openid/config/environments/production.rb +19 -0
  38. data/examples/rails_openid/config/environments/test.rb +19 -0
  39. data/examples/rails_openid/config/routes.rb +24 -0
  40. data/examples/rails_openid/doc/README_FOR_APP +2 -0
  41. data/examples/rails_openid/public/.htaccess +40 -0
  42. data/examples/rails_openid/public/404.html +8 -0
  43. data/examples/rails_openid/public/500.html +8 -0
  44. data/examples/rails_openid/public/dispatch.cgi +12 -0
  45. data/examples/rails_openid/public/dispatch.fcgi +26 -0
  46. data/examples/rails_openid/public/dispatch.rb +12 -0
  47. data/examples/rails_openid/public/favicon.ico +0 -0
  48. data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
  49. data/examples/rails_openid/public/javascripts/controls.js +750 -0
  50. data/examples/rails_openid/public/javascripts/dragdrop.js +584 -0
  51. data/examples/rails_openid/public/javascripts/effects.js +854 -0
  52. data/examples/rails_openid/public/javascripts/prototype.js +1785 -0
  53. data/examples/rails_openid/public/robots.txt +1 -0
  54. data/examples/rails_openid/script/about +3 -0
  55. data/examples/rails_openid/script/breakpointer +3 -0
  56. data/examples/rails_openid/script/console +3 -0
  57. data/examples/rails_openid/script/destroy +3 -0
  58. data/examples/rails_openid/script/generate +3 -0
  59. data/examples/rails_openid/script/performance/benchmarker +3 -0
  60. data/examples/rails_openid/script/performance/profiler +3 -0
  61. data/examples/rails_openid/script/plugin +3 -0
  62. data/examples/rails_openid/script/process/reaper +3 -0
  63. data/examples/rails_openid/script/process/spawner +3 -0
  64. data/examples/rails_openid/script/process/spinner +3 -0
  65. data/examples/rails_openid/script/runner +3 -0
  66. data/examples/rails_openid/script/server +3 -0
  67. data/examples/rails_openid/test/functional/login_controller_test.rb +18 -0
  68. data/examples/rails_openid/test/functional/server_controller_test.rb +18 -0
  69. data/examples/rails_openid/test/test_helper.rb +28 -0
  70. data/lib/hmac/hmac.rb +112 -0
  71. data/lib/hmac/sha1.rb +11 -0
  72. data/lib/hmac/sha2.rb +25 -0
  73. data/lib/openid/association.rb +249 -0
  74. data/lib/openid/consumer/associationmanager.rb +344 -0
  75. data/lib/openid/consumer/checkid_request.rb +186 -0
  76. data/lib/openid/consumer/discovery.rb +498 -0
  77. data/lib/openid/consumer/discovery_manager.rb +123 -0
  78. data/lib/openid/consumer/html_parse.rb +134 -0
  79. data/lib/openid/consumer/idres.rb +523 -0
  80. data/lib/openid/consumer/responses.rb +148 -0
  81. data/lib/openid/consumer.rb +395 -0
  82. data/lib/openid/cryptutil.rb +97 -0
  83. data/lib/openid/dh.rb +89 -0
  84. data/lib/openid/extension.rb +39 -0
  85. data/lib/openid/extensions/ax.rb +516 -0
  86. data/lib/openid/extensions/oauth.rb +91 -0
  87. data/lib/openid/extensions/pape.rb +179 -0
  88. data/lib/openid/extensions/sreg.rb +277 -0
  89. data/lib/openid/extras.rb +11 -0
  90. data/lib/openid/fetchers.rb +238 -0
  91. data/lib/openid/kvform.rb +136 -0
  92. data/lib/openid/kvpost.rb +58 -0
  93. data/lib/openid/message.rb +553 -0
  94. data/lib/openid/protocolerror.rb +8 -0
  95. data/lib/openid/server.rb +1544 -0
  96. data/lib/openid/store/filesystem.rb +271 -0
  97. data/lib/openid/store/interface.rb +75 -0
  98. data/lib/openid/store/memcache.rb +107 -0
  99. data/lib/openid/store/memory.rb +84 -0
  100. data/lib/openid/store/nonce.rb +68 -0
  101. data/lib/openid/trustroot.rb +349 -0
  102. data/lib/openid/urinorm.rb +75 -0
  103. data/lib/openid/util.rb +110 -0
  104. data/lib/openid/yadis/accept.rb +148 -0
  105. data/lib/openid/yadis/constants.rb +21 -0
  106. data/lib/openid/yadis/discovery.rb +153 -0
  107. data/lib/openid/yadis/filters.rb +205 -0
  108. data/lib/openid/yadis/htmltokenizer.rb +305 -0
  109. data/lib/openid/yadis/parsehtml.rb +45 -0
  110. data/lib/openid/yadis/services.rb +42 -0
  111. data/lib/openid/yadis/xrds.rb +155 -0
  112. data/lib/openid/yadis/xri.rb +90 -0
  113. data/lib/openid/yadis/xrires.rb +106 -0
  114. data/lib/openid.rb +20 -0
  115. data/setup.rb +1551 -0
  116. data/test/data/accept.txt +124 -0
  117. data/test/data/dh.txt +29 -0
  118. data/test/data/example-xrds.xml +14 -0
  119. data/test/data/linkparse.txt +587 -0
  120. data/test/data/n2b64 +650 -0
  121. data/test/data/test1-discover.txt +137 -0
  122. data/test/data/test1-parsehtml.txt +152 -0
  123. data/test/data/test_discover/malformed_meta_tag.html +19 -0
  124. data/test/data/test_discover/openid.html +11 -0
  125. data/test/data/test_discover/openid2.html +11 -0
  126. data/test/data/test_discover/openid2_xrds.xml +12 -0
  127. data/test/data/test_discover/openid2_xrds_no_local_id.xml +11 -0
  128. data/test/data/test_discover/openid_1_and_2.html +11 -0
  129. data/test/data/test_discover/openid_1_and_2_xrds.xml +16 -0
  130. data/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
  131. data/test/data/test_discover/openid_and_yadis.html +12 -0
  132. data/test/data/test_discover/openid_no_delegate.html +10 -0
  133. data/test/data/test_discover/yadis_0entries.xml +12 -0
  134. data/test/data/test_discover/yadis_2_bad_local_id.xml +15 -0
  135. data/test/data/test_discover/yadis_2entries_delegate.xml +22 -0
  136. data/test/data/test_discover/yadis_2entries_idp.xml +21 -0
  137. data/test/data/test_discover/yadis_another_delegate.xml +14 -0
  138. data/test/data/test_discover/yadis_idp.xml +12 -0
  139. data/test/data/test_discover/yadis_idp_delegate.xml +13 -0
  140. data/test/data/test_discover/yadis_no_delegate.xml +11 -0
  141. data/test/data/test_xrds/=j3h.2007.11.14.xrds +25 -0
  142. data/test/data/test_xrds/README +12 -0
  143. data/test/data/test_xrds/delegated-20060809-r1.xrds +34 -0
  144. data/test/data/test_xrds/delegated-20060809-r2.xrds +34 -0
  145. data/test/data/test_xrds/delegated-20060809.xrds +34 -0
  146. data/test/data/test_xrds/no-xrd.xml +7 -0
  147. data/test/data/test_xrds/not-xrds.xml +2 -0
  148. data/test/data/test_xrds/prefixsometimes.xrds +34 -0
  149. data/test/data/test_xrds/ref.xrds +109 -0
  150. data/test/data/test_xrds/sometimesprefix.xrds +34 -0
  151. data/test/data/test_xrds/spoof1.xrds +25 -0
  152. data/test/data/test_xrds/spoof2.xrds +25 -0
  153. data/test/data/test_xrds/spoof3.xrds +37 -0
  154. data/test/data/test_xrds/status222.xrds +9 -0
  155. data/test/data/test_xrds/subsegments.xrds +58 -0
  156. data/test/data/test_xrds/valid-populated-xrds.xml +39 -0
  157. data/test/data/trustroot.txt +153 -0
  158. data/test/data/urinorm.txt +79 -0
  159. data/test/discoverdata.rb +131 -0
  160. data/test/test_accept.rb +170 -0
  161. data/test/test_association.rb +266 -0
  162. data/test/test_associationmanager.rb +917 -0
  163. data/test/test_ax.rb +648 -0
  164. data/test/test_checkid_request.rb +294 -0
  165. data/test/test_consumer.rb +257 -0
  166. data/test/test_cryptutil.rb +119 -0
  167. data/test/test_dh.rb +86 -0
  168. data/test/test_discover.rb +838 -0
  169. data/test/test_discovery_manager.rb +262 -0
  170. data/test/test_extension.rb +46 -0
  171. data/test/test_extras.rb +35 -0
  172. data/test/test_fetchers.rb +538 -0
  173. data/test/test_filters.rb +270 -0
  174. data/test/test_idres.rb +963 -0
  175. data/test/test_kvform.rb +165 -0
  176. data/test/test_kvpost.rb +65 -0
  177. data/test/test_linkparse.rb +101 -0
  178. data/test/test_message.rb +1116 -0
  179. data/test/test_nonce.rb +89 -0
  180. data/test/test_oauth.rb +175 -0
  181. data/test/test_openid_yadis.rb +178 -0
  182. data/test/test_pape.rb +247 -0
  183. data/test/test_parsehtml.rb +80 -0
  184. data/test/test_responses.rb +63 -0
  185. data/test/test_server.rb +2457 -0
  186. data/test/test_sreg.rb +479 -0
  187. data/test/test_stores.rb +298 -0
  188. data/test/test_trustroot.rb +113 -0
  189. data/test/test_urinorm.rb +35 -0
  190. data/test/test_util.rb +145 -0
  191. data/test/test_xrds.rb +169 -0
  192. data/test/test_xri.rb +48 -0
  193. data/test/test_xrires.rb +63 -0
  194. data/test/test_yadis_discovery.rb +220 -0
  195. data/test/testutil.rb +127 -0
  196. data/test/util.rb +53 -0
  197. metadata +316 -0
@@ -0,0 +1,349 @@
1
+ require 'uri'
2
+ require 'openid/urinorm'
3
+
4
+ module OpenID
5
+
6
+ class RealmVerificationRedirected < Exception
7
+ # Attempting to verify this realm resulted in a redirect.
8
+ def initialize(relying_party_url, rp_url_after_redirects)
9
+ @relying_party_url = relying_party_url
10
+ @rp_url_after_redirects = rp_url_after_redirects
11
+ end
12
+
13
+ def to_s
14
+ return "Attempting to verify #{@relying_party_url} resulted in " +
15
+ "redirect to #{@rp_url_after_redirects}"
16
+ end
17
+ end
18
+
19
+ module TrustRoot
20
+ TOP_LEVEL_DOMAINS = %w'
21
+ ac ad ae aero af ag ai al am an ao aq ar arpa as asia at
22
+ au aw ax az ba bb bd be bf bg bh bi biz bj bm bn bo br bs bt
23
+ bv bw by bz ca cat cc cd cf cg ch ci ck cl cm cn co com coop
24
+ cr cu cv cx cy cz de dj dk dm do dz ec edu ee eg er es et eu
25
+ fi fj fk fm fo fr ga gb gd ge gf gg gh gi gl gm gn gov gp gq
26
+ gr gs gt gu gw gy hk hm hn hr ht hu id ie il im in info int
27
+ io iq ir is it je jm jo jobs jp ke kg kh ki km kn kp kr kw
28
+ ky kz la lb lc li lk lr ls lt lu lv ly ma mc md me mg mh mil
29
+ mk ml mm mn mo mobi mp mq mr ms mt mu museum mv mw mx my mz
30
+ na name nc ne net nf ng ni nl no np nr nu nz om org pa pe pf
31
+ pg ph pk pl pm pn pr pro ps pt pw py qa re ro rs ru rw sa sb
32
+ sc sd se sg sh si sj sk sl sm sn so sr st su sv sy sz tc td
33
+ tel tf tg th tj tk tl tm tn to tp tr travel tt tv tw tz ua
34
+ ug uk us uy uz va vc ve vg vi vn vu wf ws xn--0zwm56d
35
+ xn--11b5bs3a9aj6g xn--80akhbyknj4f xn--9t4b11yi5a
36
+ xn--deba0ad xn--g6w251d xn--hgbk6aj7f53bba
37
+ xn--hlcj6aya9esc7a xn--jxalpdlp xn--kgbechtv xn--zckzah ye
38
+ yt yu za zm zw'
39
+
40
+ ALLOWED_PROTOCOLS = ['http', 'https']
41
+
42
+ # The URI for relying party discovery, used in realm verification.
43
+ #
44
+ # XXX: This should probably live somewhere else (like in
45
+ # OpenID or OpenID::Yadis somewhere)
46
+ RP_RETURN_TO_URL_TYPE = 'http://specs.openid.net/auth/2.0/return_to'
47
+
48
+ # If the endpoint is a relying party OpenID return_to endpoint,
49
+ # return the endpoint URL. Otherwise, return None.
50
+ #
51
+ # This function is intended to be used as a filter for the Yadis
52
+ # filtering interface.
53
+ #
54
+ # endpoint: An XRDS BasicServiceEndpoint, as returned by
55
+ # performing Yadis dicovery.
56
+ #
57
+ # returns the endpoint URL or None if the endpoint is not a
58
+ # relying party endpoint.
59
+ def TrustRoot._extract_return_url(endpoint)
60
+ if endpoint.matchTypes([RP_RETURN_TO_URL_TYPE])
61
+ return endpoint.uri
62
+ else
63
+ return nil
64
+ end
65
+ end
66
+
67
+ # Is the return_to URL under one of the supplied allowed
68
+ # return_to URLs?
69
+ def TrustRoot.return_to_matches(allowed_return_to_urls, return_to)
70
+ allowed_return_to_urls.each { |allowed_return_to|
71
+ # A return_to pattern works the same as a realm, except that
72
+ # it's not allowed to use a wildcard. We'll model this by
73
+ # parsing it as a realm, and not trying to match it if it has
74
+ # a wildcard.
75
+
76
+ return_realm = TrustRoot.parse(allowed_return_to)
77
+ if (# Parses as a trust root
78
+ !return_realm.nil? and
79
+
80
+ # Does not have a wildcard
81
+ !return_realm.wildcard and
82
+
83
+ # Matches the return_to that we passed in with it
84
+ return_realm.validate_url(return_to)
85
+ )
86
+ return true
87
+ end
88
+ }
89
+
90
+ # No URL in the list matched
91
+ return false
92
+ end
93
+
94
+ # Given a relying party discovery URL return a list of return_to
95
+ # URLs.
96
+ def TrustRoot.get_allowed_return_urls(relying_party_url)
97
+ rp_url_after_redirects, return_to_urls = services.get_service_endpoints(
98
+ relying_party_url, _extract_return_url)
99
+
100
+ if rp_url_after_redirects != relying_party_url
101
+ # Verification caused a redirect
102
+ raise RealmVerificationRedirected.new(
103
+ relying_party_url, rp_url_after_redirects)
104
+ end
105
+
106
+ return return_to_urls
107
+ end
108
+
109
+ # Verify that a return_to URL is valid for the given realm.
110
+ #
111
+ # This function builds a discovery URL, performs Yadis discovery
112
+ # on it, makes sure that the URL does not redirect, parses out
113
+ # the return_to URLs, and finally checks to see if the current
114
+ # return_to URL matches the return_to.
115
+ #
116
+ # raises DiscoveryFailure when Yadis discovery fails returns
117
+ # true if the return_to URL is valid for the realm
118
+ def TrustRoot.verify_return_to(realm_str, return_to, _vrfy=nil)
119
+ # _vrfy parameter is there to make testing easier
120
+ if _vrfy.nil?
121
+ _vrfy = self.method('get_allowed_return_urls')
122
+ end
123
+
124
+ if !(_vrfy.is_a?(Proc) or _vrfy.is_a?(Method))
125
+ raise ArgumentError, "_vrfy must be a Proc or Method"
126
+ end
127
+
128
+ realm = TrustRoot.parse(realm_str)
129
+ if realm.nil?
130
+ # The realm does not parse as a URL pattern
131
+ return false
132
+ end
133
+
134
+ begin
135
+ allowable_urls = _vrfy.call(realm.build_discovery_url())
136
+ rescue RealmVerificationRedirected => err
137
+ Util.log(err.to_s)
138
+ return false
139
+ end
140
+
141
+ if return_to_matches(allowable_urls, return_to)
142
+ return true
143
+ else
144
+ Util.log("Failed to validate return_to #{return_to} for " +
145
+ "realm #{realm_str}, was not in #{allowable_urls}")
146
+ return false
147
+ end
148
+ end
149
+
150
+ class TrustRoot
151
+
152
+ attr_reader :unparsed, :proto, :wildcard, :host, :port, :path
153
+
154
+ @@empty_re = Regexp.new('^http[s]*:\/\/\*\/$')
155
+
156
+ def TrustRoot._build_path(path, query=nil, frag=nil)
157
+ s = path.dup
158
+
159
+ frag = nil if frag == ''
160
+ query = nil if query == ''
161
+
162
+ if query
163
+ s << "?" << query
164
+ end
165
+
166
+ if frag
167
+ s << "#" << frag
168
+ end
169
+
170
+ return s
171
+ end
172
+
173
+ def TrustRoot._parse_url(url)
174
+ begin
175
+ url = URINorm.urinorm(url)
176
+ rescue URI::InvalidURIError => err
177
+ nil
178
+ end
179
+
180
+ begin
181
+ parsed = URI::parse(url)
182
+ rescue URI::InvalidURIError
183
+ return nil
184
+ end
185
+
186
+ path = TrustRoot._build_path(parsed.path,
187
+ parsed.query,
188
+ parsed.fragment)
189
+
190
+ return [parsed.scheme || '', parsed.host || '',
191
+ parsed.port || '', path || '']
192
+ end
193
+
194
+ def TrustRoot.parse(trust_root)
195
+ trust_root = trust_root.dup
196
+ unparsed = trust_root.dup
197
+
198
+ # look for wildcard
199
+ wildcard = (not trust_root.index('://*.').nil?)
200
+ trust_root.sub!('*.', '') if wildcard
201
+
202
+ # handle http://*/ case
203
+ if not wildcard and @@empty_re.match(trust_root)
204
+ proto = trust_root.split(':')[0]
205
+ port = proto == 'http' ? 80 : 443
206
+ return new(unparsed, proto, true, '', port, '/')
207
+ end
208
+
209
+ parts = TrustRoot._parse_url(trust_root)
210
+ return nil if parts.nil?
211
+
212
+ proto, host, port, path = parts
213
+
214
+ # check for URI fragment
215
+ if path and !path.index('#').nil?
216
+ return nil
217
+ end
218
+
219
+ return nil unless ['http', 'https'].member?(proto)
220
+ return new(unparsed, proto, wildcard, host, port, path)
221
+ end
222
+
223
+ def TrustRoot.check_sanity(trust_root_string)
224
+ trust_root = TrustRoot.parse(trust_root_string)
225
+ if trust_root.nil?
226
+ return false
227
+ else
228
+ return trust_root.sane?
229
+ end
230
+ end
231
+
232
+ # quick func for validating a url against a trust root. See the
233
+ # TrustRoot class if you need more control.
234
+ def self.check_url(trust_root, url)
235
+ tr = self.parse(trust_root)
236
+ return (!tr.nil? and tr.validate_url(url))
237
+ end
238
+
239
+ # Return a discovery URL for this realm.
240
+ #
241
+ # This function does not check to make sure that the realm is
242
+ # valid. Its behaviour on invalid inputs is undefined.
243
+ #
244
+ # return_to:: The relying party return URL of the OpenID
245
+ # authentication request
246
+ #
247
+ # Returns the URL upon which relying party discovery should be
248
+ # run in order to verify the return_to URL
249
+ def build_discovery_url
250
+ if self.wildcard
251
+ # Use "www." in place of the star
252
+ www_domain = 'www.' + @host
253
+ port = (!@port.nil? and ![80, 443].member?(@port)) ? (":" + @port.to_s) : ''
254
+ return "#{@proto}://#{www_domain}#{port}#{@path}"
255
+ else
256
+ return @unparsed
257
+ end
258
+ end
259
+
260
+ def initialize(unparsed, proto, wildcard, host, port, path)
261
+ @unparsed = unparsed
262
+ @proto = proto
263
+ @wildcard = wildcard
264
+ @host = host
265
+ @port = port
266
+ @path = path
267
+ end
268
+
269
+ def sane?
270
+ return true if @host == 'localhost'
271
+
272
+ host_parts = @host.split('.')
273
+
274
+ # a note: ruby string split does not put an empty string at
275
+ # the end of the list if the split element is last. for
276
+ # example, 'foo.com.'.split('.') => ['foo','com']. Mentioned
277
+ # because the python code differs here.
278
+
279
+ return false if host_parts.length == 0
280
+
281
+ # no adjacent dots
282
+ return false if host_parts.member?('')
283
+
284
+ # last part must be a tld
285
+ tld = host_parts[-1]
286
+ return false unless TOP_LEVEL_DOMAINS.member?(tld)
287
+
288
+ return false if host_parts.length == 1
289
+
290
+ if @wildcard
291
+ if tld.length == 2 and host_parts[-2].length <= 3
292
+ # It's a 2-letter tld with a short second to last segment
293
+ # so there needs to be more than two segments specified
294
+ # (e.g. *.co.uk is insane)
295
+ return host_parts.length > 2
296
+ end
297
+ end
298
+
299
+ return true
300
+ end
301
+
302
+ def validate_url(url)
303
+ parts = TrustRoot._parse_url(url)
304
+ return false if parts.nil?
305
+
306
+ proto, host, port, path = parts
307
+
308
+ return false unless proto == @proto
309
+ return false unless port == @port
310
+ return false unless host.index('*').nil?
311
+
312
+ if !@wildcard
313
+ if host != @host
314
+ return false
315
+ end
316
+ elsif ((@host != '') and
317
+ (!host.ends_with?('.' + @host)) and
318
+ (host != @host))
319
+ return false
320
+ end
321
+
322
+ if path != @path
323
+ path_len = @path.length
324
+ trust_prefix = @path[0...path_len]
325
+ url_prefix = path[0...path_len]
326
+
327
+ # must be equal up to the length of the path, at least
328
+ if trust_prefix != url_prefix
329
+ return false
330
+ end
331
+
332
+ # These characters must be on the boundary between the end
333
+ # of the trust root's path and the start of the URL's path.
334
+ if !@path.index('?').nil?
335
+ allowed = '&'
336
+ else
337
+ allowed = '?/'
338
+ end
339
+
340
+ return (!allowed.index(@path[-1]).nil? or
341
+ !allowed.index(path[path_len]).nil?)
342
+ end
343
+
344
+ return true
345
+ end
346
+ end
347
+ end
348
+ end
349
+
@@ -0,0 +1,75 @@
1
+ require 'uri'
2
+
3
+ require "openid/extras"
4
+
5
+ module OpenID
6
+
7
+ module URINorm
8
+ public
9
+ def URINorm.urinorm(uri)
10
+ uri = URI.parse(uri)
11
+
12
+ raise URI::InvalidURIError.new('no scheme') unless uri.scheme
13
+ uri.scheme = uri.scheme.downcase
14
+ unless ['http','https'].member?(uri.scheme)
15
+ raise URI::InvalidURIError.new('Not an HTTP or HTTPS URI')
16
+ end
17
+
18
+ raise URI::InvalidURIError.new('no host') unless uri.host
19
+ uri.host = uri.host.downcase
20
+
21
+ uri.path = remove_dot_segments(uri.path)
22
+ uri.path = '/' if uri.path.length == 0
23
+
24
+ uri = uri.normalize.to_s
25
+ uri = uri.gsub(PERCENT_ESCAPE_RE) {
26
+ sub = $&[1..2].to_i(16).chr
27
+ reserved(sub) ? $&.upcase : sub
28
+ }
29
+
30
+ return uri
31
+ end
32
+
33
+ private
34
+ RESERVED_RE = /[A-Za-z0-9._~-]/
35
+ PERCENT_ESCAPE_RE = /%[0-9a-zA-Z]{2}/
36
+
37
+ def URINorm.reserved(chr)
38
+ not RESERVED_RE =~ chr
39
+ end
40
+
41
+ def URINorm.remove_dot_segments(path)
42
+ result_segments = []
43
+
44
+ while path.length > 0
45
+ if path.starts_with?('../')
46
+ path = path[3..-1]
47
+ elsif path.starts_with?('./')
48
+ path = path[2..-1]
49
+ elsif path.starts_with?('/./')
50
+ path = path[2..-1]
51
+ elsif path == '/.'
52
+ path = '/'
53
+ elsif path.starts_with?('/../')
54
+ path = path[3..-1]
55
+ result_segments.pop if result_segments.length > 0
56
+ elsif path == '/..'
57
+ path = '/'
58
+ result_segments.pop if result_segments.length > 0
59
+ elsif path == '..' or path == '.'
60
+ path = ''
61
+ else
62
+ i = 0
63
+ i = 1 if path[0].chr == '/'
64
+ i = path.index('/', i)
65
+ i = path.length if i.nil?
66
+ result_segments << path[0...i]
67
+ path = path[i..-1]
68
+ end
69
+ end
70
+
71
+ return result_segments.join('')
72
+ end
73
+ end
74
+
75
+ end
@@ -0,0 +1,110 @@
1
+ require "cgi"
2
+ require "uri"
3
+ require "logger"
4
+
5
+ require "openid/extras"
6
+
7
+ # See OpenID::Consumer or OpenID::Server modules, as well as the store classes
8
+ module OpenID
9
+ class AssertionError < Exception
10
+ end
11
+
12
+ # Exceptions that are raised by the library are subclasses of this
13
+ # exception type, so if you want to catch all exceptions raised by
14
+ # the library, you can catch OpenIDError
15
+ class OpenIDError < StandardError
16
+ end
17
+
18
+ module Util
19
+
20
+ BASE64_CHARS = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' \
21
+ 'abcdefghijklmnopqrstuvwxyz0123456789+/')
22
+ BASE64_RE = Regexp.compile("
23
+ \\A
24
+ ([#{BASE64_CHARS}]{4})*
25
+ ([#{BASE64_CHARS}]{2}==|
26
+ [#{BASE64_CHARS}]{3}=)?
27
+ \\Z", Regexp::EXTENDED)
28
+
29
+ def Util.assert(value, message=nil)
30
+ if not value
31
+ raise AssertionError, message or value
32
+ end
33
+ end
34
+
35
+ def Util.to_base64(s)
36
+ [s].pack('m').gsub("\n", "")
37
+ end
38
+
39
+ def Util.from_base64(s)
40
+ without_newlines = s.gsub(/[\r\n]+/, '')
41
+ if !BASE64_RE.match(without_newlines)
42
+ raise ArgumentError, "Malformed input: #{s.inspect}"
43
+ end
44
+ without_newlines.unpack('m').first
45
+ end
46
+
47
+ def Util.urlencode(args)
48
+ a = []
49
+ args.each do |key, val|
50
+ val = '' unless val
51
+ a << (CGI::escape(key) + "=" + CGI::escape(val))
52
+ end
53
+ a.join("&")
54
+ end
55
+
56
+ def Util.parse_query(qs)
57
+ query = {}
58
+ CGI::parse(qs).each {|k,v| query[k] = v[0]}
59
+ return query
60
+ end
61
+
62
+ def Util.append_args(url, args)
63
+ url = url.dup
64
+ return url if args.length == 0
65
+
66
+ if args.respond_to?('each_pair')
67
+ args = args.sort
68
+ end
69
+
70
+ url << (url.include?("?") ? "&" : "?")
71
+ url << Util.urlencode(args)
72
+ end
73
+
74
+ @@logger = Logger.new(STDERR)
75
+ @@logger.progname = "OpenID"
76
+
77
+ def Util.logger=(logger)
78
+ @@logger = logger
79
+ end
80
+
81
+ def Util.logger
82
+ @@logger
83
+ end
84
+
85
+ # change the message below to do whatever you like for logging
86
+ def Util.log(message)
87
+ logger.info(message)
88
+ end
89
+
90
+ def Util.auto_submit_html(form, title='OpenID transaction in progress')
91
+ return "
92
+ <html>
93
+ <head>
94
+ <title>#{title}</title>
95
+ </head>
96
+ <body onload='document.forms[0].submit();'>
97
+ #{form}
98
+ <script>
99
+ var elements = document.forms[0].elements;
100
+ for (var i = 0; i < elements.length; i++) {
101
+ elements[i].style.display = \"none\";
102
+ }
103
+ </script>
104
+ </body>
105
+ </html>
106
+ "
107
+ end
108
+ end
109
+
110
+ end
@@ -0,0 +1,148 @@
1
+ module OpenID
2
+
3
+ module Yadis
4
+
5
+ # Generate an accept header value
6
+ #
7
+ # [str or (str, float)] -> str
8
+ def self.generate_accept_header(*elements)
9
+ parts = []
10
+ elements.each { |element|
11
+ if element.is_a?(String)
12
+ qs = "1.0"
13
+ mtype = element
14
+ else
15
+ mtype, q = element
16
+ q = q.to_f
17
+ if q > 1 or q <= 0
18
+ raise ArgumentError.new("Invalid preference factor: #{q}")
19
+ end
20
+ qs = sprintf("%0.1f", q)
21
+ end
22
+
23
+ parts << [qs, mtype]
24
+ }
25
+
26
+ parts.sort!
27
+ chunks = []
28
+ parts.each { |q, mtype|
29
+ if q == '1.0'
30
+ chunks << mtype
31
+ else
32
+ chunks << sprintf("%s; q=%s", mtype, q)
33
+ end
34
+ }
35
+
36
+ return chunks.join(', ')
37
+ end
38
+
39
+ def self.parse_accept_header(value)
40
+ # Parse an accept header, ignoring any accept-extensions
41
+ #
42
+ # returns a list of tuples containing main MIME type, MIME
43
+ # subtype, and quality markdown.
44
+ #
45
+ # str -> [(str, str, float)]
46
+ chunks = value.split(',', -1).collect { |v| v.strip }
47
+ accept = []
48
+ chunks.each { |chunk|
49
+ parts = chunk.split(";", -1).collect { |s| s.strip }
50
+
51
+ mtype = parts.shift
52
+ if mtype.index('/').nil?
53
+ # This is not a MIME type, so ignore the bad data
54
+ next
55
+ end
56
+
57
+ main, sub = mtype.split('/', 2)
58
+
59
+ q = nil
60
+ parts.each { |ext|
61
+ if !ext.index('=').nil?
62
+ k, v = ext.split('=', 2)
63
+ if k == 'q'
64
+ q = v.to_f
65
+ end
66
+ end
67
+ }
68
+
69
+ q = 1.0 if q.nil?
70
+
71
+ accept << [q, main, sub]
72
+ }
73
+
74
+ accept.sort!
75
+ accept.reverse!
76
+
77
+ return accept.collect { |q, main, sub| [main, sub, q] }
78
+ end
79
+
80
+ def self.match_types(accept_types, have_types)
81
+ # Given the result of parsing an Accept: header, and the
82
+ # available MIME types, return the acceptable types with their
83
+ # quality markdowns.
84
+ #
85
+ # For example:
86
+ #
87
+ # >>> acceptable = parse_accept_header('text/html, text/plain; q=0.5')
88
+ # >>> matchTypes(acceptable, ['text/plain', 'text/html', 'image/jpeg'])
89
+ # [('text/html', 1.0), ('text/plain', 0.5)]
90
+ #
91
+ # Type signature: ([(str, str, float)], [str]) -> [(str, float)]
92
+ if accept_types.nil? or accept_types == []
93
+ # Accept all of them
94
+ default = 1
95
+ else
96
+ default = 0
97
+ end
98
+
99
+ match_main = {}
100
+ match_sub = {}
101
+ accept_types.each { |main, sub, q|
102
+ if main == '*'
103
+ default = [default, q].max
104
+ next
105
+ elsif sub == '*'
106
+ match_main[main] = [match_main.fetch(main, 0), q].max
107
+ else
108
+ match_sub[[main, sub]] = [match_sub.fetch([main, sub], 0), q].max
109
+ end
110
+ }
111
+
112
+ accepted_list = []
113
+ order_maintainer = 0
114
+ have_types.each { |mtype|
115
+ main, sub = mtype.split('/', 2)
116
+ if match_sub.member?([main, sub])
117
+ q = match_sub[[main, sub]]
118
+ else
119
+ q = match_main.fetch(main, default)
120
+ end
121
+
122
+ if q != 0
123
+ accepted_list << [1 - q, order_maintainer, q, mtype]
124
+ order_maintainer += 1
125
+ end
126
+ }
127
+
128
+ accepted_list.sort!
129
+ return accepted_list.collect { |_, _, q, mtype| [mtype, q] }
130
+ end
131
+
132
+ def self.get_acceptable(accept_header, have_types)
133
+ # Parse the accept header and return a list of available types
134
+ # in preferred order. If a type is unacceptable, it will not be
135
+ # in the resulting list.
136
+ #
137
+ # This is a convenience wrapper around matchTypes and
138
+ # parse_accept_header
139
+ #
140
+ # (str, [str]) -> [str]
141
+ accepted = self.parse_accept_header(accept_header)
142
+ preferred = self.match_types(accepted, have_types)
143
+ return preferred.collect { |mtype, _| mtype }
144
+ end
145
+
146
+ end
147
+
148
+ end
@@ -0,0 +1,21 @@
1
+
2
+ require 'openid/yadis/accept'
3
+
4
+ module OpenID
5
+
6
+ module Yadis
7
+
8
+ YADIS_HEADER_NAME = 'X-XRDS-Location'
9
+ YADIS_CONTENT_TYPE = 'application/xrds+xml'
10
+
11
+ # A value suitable for using as an accept header when performing
12
+ # YADIS discovery, unless the application has special requirements
13
+ YADIS_ACCEPT_HEADER = generate_accept_header(
14
+ ['text/html', 0.3],
15
+ ['application/xhtml+xml', 0.5],
16
+ [YADIS_CONTENT_TYPE, 1.0]
17
+ )
18
+
19
+ end
20
+
21
+ end