ruby-openid 1.1.4 → 2.0.1

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 (207) hide show
  1. data/INSTALL +0 -9
  2. data/README +21 -22
  3. data/UPGRADE +117 -0
  4. data/admin/runtests.rb +36 -0
  5. data/examples/README +13 -21
  6. data/examples/active_record_openid_store/README +8 -3
  7. data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +4 -8
  8. data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
  9. data/examples/active_record_openid_store/lib/association.rb +2 -0
  10. data/examples/active_record_openid_store/lib/openid_ar_store.rb +22 -47
  11. data/examples/active_record_openid_store/test/store_test.rb +78 -48
  12. data/examples/discover +46 -0
  13. data/examples/{rails_server → rails_openid}/README +0 -0
  14. data/examples/{rails_server → rails_openid}/Rakefile +0 -0
  15. data/examples/{rails_server → rails_openid}/app/controllers/application.rb +0 -0
  16. data/examples/rails_openid/app/controllers/consumer_controller.rb +115 -0
  17. data/examples/{rails_server → rails_openid}/app/controllers/login_controller.rb +10 -2
  18. data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
  19. data/examples/{rails_server → rails_openid}/app/helpers/application_helper.rb +0 -0
  20. data/examples/{rails_server → rails_openid}/app/helpers/login_helper.rb +0 -0
  21. data/examples/{rails_server → rails_openid}/app/helpers/server_helper.rb +0 -0
  22. data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
  23. data/examples/rails_openid/app/views/consumer/start.rhtml +8 -0
  24. data/examples/{rails_server → rails_openid}/app/views/layouts/server.rhtml +0 -0
  25. data/examples/{rails_server → rails_openid}/app/views/login/index.rhtml +1 -1
  26. data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
  27. data/examples/{rails_server → rails_openid}/config/boot.rb +0 -0
  28. data/examples/{rails_server → rails_openid}/config/database.yml +0 -0
  29. data/examples/{rails_server → rails_openid}/config/environment.rb +0 -0
  30. data/examples/{rails_server → rails_openid}/config/environments/development.rb +0 -0
  31. data/examples/{rails_server → rails_openid}/config/environments/production.rb +0 -0
  32. data/examples/{rails_server → rails_openid}/config/environments/test.rb +0 -0
  33. data/examples/{rails_server → rails_openid}/config/routes.rb +2 -1
  34. data/examples/{rails_server → rails_openid}/doc/README_FOR_APP +0 -0
  35. data/examples/{rails_server → rails_openid}/public/404.html +0 -0
  36. data/examples/{rails_server → rails_openid}/public/500.html +0 -0
  37. data/examples/{rails_server → rails_openid}/public/dispatch.cgi +0 -0
  38. data/examples/{rails_server → rails_openid}/public/dispatch.fcgi +0 -0
  39. data/examples/{rails_server → rails_openid}/public/dispatch.rb +0 -0
  40. data/examples/{rails_server → rails_openid}/public/favicon.ico +0 -0
  41. data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
  42. data/examples/{rails_server → rails_openid}/public/javascripts/controls.js +0 -0
  43. data/examples/{rails_server → rails_openid}/public/javascripts/dragdrop.js +0 -0
  44. data/examples/{rails_server → rails_openid}/public/javascripts/effects.js +0 -0
  45. data/examples/{rails_server → rails_openid}/public/javascripts/prototype.js +0 -0
  46. data/examples/{rails_server → rails_openid}/public/robots.txt +0 -0
  47. data/examples/{rails_server → rails_openid}/script/about +0 -0
  48. data/examples/{rails_server → rails_openid}/script/breakpointer +0 -0
  49. data/examples/{rails_server → rails_openid}/script/console +0 -0
  50. data/examples/{rails_server → rails_openid}/script/destroy +0 -0
  51. data/examples/{rails_server → rails_openid}/script/generate +0 -0
  52. data/examples/{rails_server → rails_openid}/script/performance/benchmarker +0 -0
  53. data/examples/{rails_server → rails_openid}/script/performance/profiler +0 -0
  54. data/examples/{rails_server → rails_openid}/script/plugin +0 -0
  55. data/examples/{rails_server → rails_openid}/script/process/reaper +0 -0
  56. data/examples/{rails_server → rails_openid}/script/process/spawner +0 -0
  57. data/examples/{rails_server → rails_openid}/script/process/spinner +0 -0
  58. data/examples/{rails_server → rails_openid}/script/runner +0 -0
  59. data/examples/{rails_server → rails_openid}/script/server +0 -0
  60. data/examples/{rails_server → rails_openid}/test/functional/login_controller_test.rb +0 -0
  61. data/examples/{rails_server → rails_openid}/test/functional/server_controller_test.rb +0 -0
  62. data/examples/{rails_server → rails_openid}/test/test_helper.rb +0 -0
  63. data/lib/{hmac.rb → hmac/hmac.rb} +0 -0
  64. data/lib/{hmac-sha1.rb → hmac/sha1.rb} +1 -1
  65. data/lib/{hmac-sha2.rb → hmac/sha2.rb} +1 -1
  66. data/lib/openid/association.rb +213 -73
  67. data/lib/openid/consumer/associationmanager.rb +338 -0
  68. data/lib/openid/consumer/checkid_request.rb +175 -0
  69. data/lib/openid/consumer/discovery.rb +480 -0
  70. data/lib/openid/consumer/discovery_manager.rb +123 -0
  71. data/lib/openid/consumer/html_parse.rb +136 -0
  72. data/lib/openid/consumer/idres.rb +525 -0
  73. data/lib/openid/consumer/responses.rb +133 -0
  74. data/lib/openid/consumer.rb +280 -807
  75. data/lib/openid/cryptutil.rb +85 -0
  76. data/lib/openid/dh.rb +60 -23
  77. data/lib/openid/extension.rb +31 -0
  78. data/lib/openid/extensions/ax.rb +506 -0
  79. data/lib/openid/extensions/pape.rb +182 -0
  80. data/lib/openid/extensions/sreg.rb +275 -0
  81. data/lib/openid/extras.rb +11 -0
  82. data/lib/openid/fetchers.rb +132 -93
  83. data/lib/openid/kvform.rb +133 -0
  84. data/lib/openid/kvpost.rb +56 -0
  85. data/lib/openid/message.rb +534 -0
  86. data/lib/openid/protocolerror.rb +6 -0
  87. data/lib/openid/server.rb +1215 -666
  88. data/lib/openid/store/filesystem.rb +271 -0
  89. data/lib/openid/store/interface.rb +75 -0
  90. data/lib/openid/store/memory.rb +84 -0
  91. data/lib/openid/store/nonce.rb +68 -0
  92. data/lib/openid/trustroot.rb +314 -87
  93. data/lib/openid/urinorm.rb +37 -34
  94. data/lib/openid/util.rb +42 -220
  95. data/lib/openid/yadis/accept.rb +148 -0
  96. data/lib/openid/yadis/constants.rb +21 -0
  97. data/lib/openid/yadis/discovery.rb +153 -0
  98. data/lib/openid/yadis/filters.rb +205 -0
  99. data/lib/openid/{htmltokenizer.rb → yadis/htmltokenizer.rb} +1 -54
  100. data/lib/openid/yadis/parsehtml.rb +36 -0
  101. data/lib/openid/yadis/services.rb +42 -0
  102. data/lib/openid/yadis/xrds.rb +171 -0
  103. data/lib/openid/yadis/xri.rb +90 -0
  104. data/lib/openid/yadis/xrires.rb +106 -0
  105. data/lib/openid.rb +1 -4
  106. data/test/data/accept.txt +124 -0
  107. data/test/data/dh.txt +29 -0
  108. data/test/data/example-xrds.xml +14 -0
  109. data/test/data/linkparse.txt +587 -0
  110. data/test/data/n2b64 +650 -0
  111. data/test/data/test1-discover.txt +137 -0
  112. data/test/data/test1-parsehtml.txt +128 -0
  113. data/test/data/test_discover/openid.html +11 -0
  114. data/test/data/test_discover/openid2.html +11 -0
  115. data/test/data/test_discover/openid2_xrds.xml +12 -0
  116. data/test/data/test_discover/openid2_xrds_no_local_id.xml +11 -0
  117. data/test/data/test_discover/openid_1_and_2.html +11 -0
  118. data/test/data/test_discover/openid_1_and_2_xrds.xml +16 -0
  119. data/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
  120. data/test/data/test_discover/openid_and_yadis.html +12 -0
  121. data/test/data/test_discover/openid_no_delegate.html +10 -0
  122. data/test/data/test_discover/yadis_0entries.xml +12 -0
  123. data/test/data/test_discover/yadis_2_bad_local_id.xml +15 -0
  124. data/test/data/test_discover/yadis_2entries_delegate.xml +22 -0
  125. data/test/data/test_discover/yadis_2entries_idp.xml +21 -0
  126. data/test/data/test_discover/yadis_another_delegate.xml +14 -0
  127. data/test/data/test_discover/yadis_idp.xml +12 -0
  128. data/test/data/test_discover/yadis_idp_delegate.xml +13 -0
  129. data/test/data/test_discover/yadis_no_delegate.xml +11 -0
  130. data/test/data/test_xrds/=j3h.2007.11.14.xrds +25 -0
  131. data/test/data/test_xrds/README +12 -0
  132. data/test/data/test_xrds/delegated-20060809-r1.xrds +34 -0
  133. data/test/data/test_xrds/delegated-20060809-r2.xrds +34 -0
  134. data/test/data/test_xrds/delegated-20060809.xrds +34 -0
  135. data/test/data/test_xrds/no-xrd.xml +7 -0
  136. data/test/data/test_xrds/not-xrds.xml +2 -0
  137. data/test/data/test_xrds/prefixsometimes.xrds +34 -0
  138. data/test/data/test_xrds/ref.xrds +109 -0
  139. data/test/data/test_xrds/sometimesprefix.xrds +34 -0
  140. data/test/data/test_xrds/spoof1.xrds +25 -0
  141. data/test/data/test_xrds/spoof2.xrds +25 -0
  142. data/test/data/test_xrds/spoof3.xrds +37 -0
  143. data/test/data/test_xrds/status222.xrds +9 -0
  144. data/test/data/test_xrds/valid-populated-xrds.xml +39 -0
  145. data/test/data/trustroot.txt +147 -0
  146. data/test/discoverdata.rb +131 -0
  147. data/test/test_accept.rb +170 -0
  148. data/test/test_association.rb +266 -0
  149. data/test/test_associationmanager.rb +899 -0
  150. data/test/test_ax.rb +587 -0
  151. data/test/test_checkid_request.rb +297 -0
  152. data/test/test_consumer.rb +257 -0
  153. data/test/test_cryptutil.rb +117 -0
  154. data/test/test_dh.rb +86 -0
  155. data/test/test_discover.rb +772 -0
  156. data/test/test_discovery_manager.rb +262 -0
  157. data/test/test_extras.rb +35 -0
  158. data/test/test_fetchers.rb +472 -0
  159. data/test/test_filters.rb +270 -0
  160. data/test/test_idres.rb +816 -0
  161. data/test/test_kvform.rb +165 -0
  162. data/test/test_kvpost.rb +65 -0
  163. data/test/test_linkparse.rb +101 -0
  164. data/test/test_message.rb +1058 -0
  165. data/test/test_nonce.rb +89 -0
  166. data/test/test_openid_yadis.rb +178 -0
  167. data/test/test_pape.rb +233 -0
  168. data/test/test_parsehtml.rb +80 -0
  169. data/test/test_responses.rb +63 -0
  170. data/test/test_server.rb +2270 -0
  171. data/test/test_sreg.rb +479 -0
  172. data/test/test_stores.rb +269 -0
  173. data/test/test_trustroot.rb +112 -0
  174. data/test/{urinorm.rb → test_urinorm.rb} +6 -3
  175. data/test/test_util.rb +144 -0
  176. data/test/test_xrds.rb +160 -0
  177. data/test/test_xri.rb +48 -0
  178. data/test/test_xrires.rb +63 -0
  179. data/test/test_yadis_discovery.rb +207 -0
  180. data/test/testutil.rb +116 -0
  181. data/test/util.rb +47 -50
  182. metadata +233 -143
  183. data/examples/consumer.rb +0 -290
  184. data/examples/rails_openid_login_generator/openid_login_generator-0.1.gem +0 -0
  185. data/examples/rails_server/app/controllers/server_controller.rb +0 -190
  186. data/examples/rails_server/app/views/server/decide.rhtml +0 -11
  187. data/examples/rails_server/public/images/rails.png +0 -0
  188. data/lib/hmac-md5.rb +0 -11
  189. data/lib/hmac-rmd160.rb +0 -11
  190. data/lib/openid/discovery.rb +0 -122
  191. data/lib/openid/filestore.rb +0 -315
  192. data/lib/openid/parse.rb +0 -23
  193. data/lib/openid/service.rb +0 -147
  194. data/lib/openid/stores.rb +0 -178
  195. data/test/assoc.rb +0 -38
  196. data/test/consumer.rb +0 -376
  197. data/test/data/brian.xrds +0 -16
  198. data/test/data/brianellin.mylid.xrds +0 -42
  199. data/test/dh.rb +0 -20
  200. data/test/extensions.rb +0 -30
  201. data/test/linkparse.rb +0 -305
  202. data/test/runtests.rb +0 -22
  203. data/test/server2.rb +0 -1053
  204. data/test/service.rb +0 -47
  205. data/test/storetestcase.rb +0 -172
  206. data/test/teststore.rb +0 -47
  207. data/test/trustroot.rb +0 -117
@@ -0,0 +1,205 @@
1
+ # This file contains functions and classes used for extracting
2
+ # endpoint information out of a Yadis XRD file using the REXML
3
+ # XML parser.
4
+
5
+ #
6
+ module OpenID
7
+ module Yadis
8
+ class BasicServiceEndpoint
9
+ attr_reader :type_uris, :yadis_url, :uri, :service_element
10
+
11
+ # Generic endpoint object that contains parsed service
12
+ # information, as well as a reference to the service element
13
+ # from which it was generated. If there is more than one
14
+ # xrd:Type or xrd:URI in the xrd:Service, this object represents
15
+ # just one of those pairs.
16
+ #
17
+ # This object can be used as a filter, because it implements
18
+ # fromBasicServiceEndpoint.
19
+ #
20
+ # The simplest kind of filter you can write implements
21
+ # fromBasicServiceEndpoint, which takes one of these objects.
22
+ def initialize(yadis_url, type_uris, uri, service_element)
23
+ @type_uris = type_uris
24
+ @yadis_url = yadis_url
25
+ @uri = uri
26
+ @service_element = service_element
27
+ end
28
+
29
+ # Query this endpoint to see if it has any of the given type
30
+ # URIs. This is useful for implementing other endpoint classes
31
+ # that e.g. need to check for the presence of multiple
32
+ # versions of a single protocol.
33
+ def match_types(type_uris)
34
+ return @type_uris & type_uris
35
+ end
36
+
37
+ # Trivial transform from a basic endpoint to itself. This
38
+ # method exists to allow BasicServiceEndpoint to be used as a
39
+ # filter.
40
+ #
41
+ # If you are subclassing this object, re-implement this function.
42
+ def self.from_basic_service_endpoint(endpoint)
43
+ return endpoint
44
+ end
45
+
46
+ # A hack to make both this class and its instances respond to
47
+ # this message since Ruby doesn't support static methods.
48
+ def from_basic_service_endpoint(endpoint)
49
+ return self.class.from_basic_service_endpoint(endpoint)
50
+ end
51
+
52
+ end
53
+
54
+ # Take a list of basic filters and makes a filter that
55
+ # transforms the basic filter into a top-level filter. This is
56
+ # mostly useful for the implementation of make_filter, which
57
+ # should only be needed for special cases or internal use by
58
+ # this library.
59
+ #
60
+ # This object is useful for creating simple filters for services
61
+ # that use one URI and are specified by one Type (we expect most
62
+ # Types will fit this paradigm).
63
+ #
64
+ # Creates a BasicServiceEndpoint object and apply the filter
65
+ # functions to it until one of them returns a value.
66
+ class TransformFilterMaker
67
+ attr_reader :filter_procs
68
+
69
+ # Initialize the filter maker's state
70
+ #
71
+ # filter_functions are the endpoint transformer
72
+ # Procs to apply to the basic endpoint. These are called in
73
+ # turn until one of them does not return nil, and the result
74
+ # of that transformer is returned.
75
+ def initialize(filter_procs)
76
+ @filter_procs = filter_procs
77
+ end
78
+
79
+ # Returns an array of endpoint objects produced by the
80
+ # filter procs.
81
+ def get_service_endpoints(yadis_url, service_element)
82
+ endpoints = []
83
+
84
+ # Do an expansion of the service element by xrd:Type and
85
+ # xrd:URI
86
+ Yadis::expand_service(service_element).each { |type_uris, uri, _|
87
+ # Create a basic endpoint object to represent this
88
+ # yadis_url, Service, Type, URI combination
89
+ endpoint = BasicServiceEndpoint.new(
90
+ yadis_url, type_uris, uri, service_element)
91
+
92
+ e = apply_filters(endpoint)
93
+ if !e.nil?
94
+ endpoints << e
95
+ end
96
+ }
97
+ return endpoints
98
+ end
99
+
100
+ def apply_filters(endpoint)
101
+ # Apply filter procs to an endpoint until one of them returns
102
+ # non-nil.
103
+ @filter_procs.each { |filter_proc|
104
+ e = filter_proc.call(endpoint)
105
+ if !e.nil?
106
+ # Once one of the filters has returned an endpoint, do not
107
+ # apply any more.
108
+ return e
109
+ end
110
+ }
111
+
112
+ return nil
113
+ end
114
+ end
115
+
116
+ class CompoundFilter
117
+ attr_reader :subfilters
118
+
119
+ # Create a new filter that applies a set of filters to an
120
+ # endpoint and collects their results.
121
+ def initialize(subfilters)
122
+ @subfilters = subfilters
123
+ end
124
+
125
+ # Generate all endpoint objects for all of the subfilters of
126
+ # this filter and return their concatenation.
127
+ def get_service_endpoints(yadis_url, service_element)
128
+ endpoints = []
129
+ @subfilters.each { |subfilter|
130
+ endpoints += subfilter.get_service_endpoints(yadis_url, service_element)
131
+ }
132
+ return endpoints
133
+ end
134
+ end
135
+
136
+ # Exception raised when something is not able to be turned into a
137
+ # filter
138
+ @@filter_type_error = TypeError.new(
139
+ 'Expected a filter, an endpoint, a callable or a list of any of these.')
140
+
141
+ # Convert a filter-convertable thing into a filter
142
+ #
143
+ # parts should be a filter, an endpoint, a callable, or a list of
144
+ # any of these.
145
+ def self.make_filter(parts)
146
+ # Convert the parts into a list, and pass to mk_compound_filter
147
+ if parts.nil?
148
+ parts = [BasicServiceEndpoint]
149
+ end
150
+
151
+ if parts.is_a?(Array)
152
+ return mk_compound_filter(parts)
153
+ else
154
+ return mk_compound_filter([parts])
155
+ end
156
+ end
157
+
158
+ # Create a filter out of a list of filter-like things
159
+ #
160
+ # Used by make_filter
161
+ #
162
+ # parts should be a list of things that can be passed to make_filter
163
+ def self.mk_compound_filter(parts)
164
+
165
+ if !parts.respond_to?('each')
166
+ raise TypeError, "#{parts.inspect} is not iterable"
167
+ end
168
+
169
+ # Separate into a list of callables and a list of filter objects
170
+ transformers = []
171
+ filters = []
172
+ parts.each { |subfilter|
173
+ if !subfilter.is_a?(Array)
174
+ # If it's not an iterable
175
+ if subfilter.respond_to?('get_service_endpoints')
176
+ # It's a full filter
177
+ filters << subfilter
178
+ elsif subfilter.respond_to?('from_basic_service_endpoint')
179
+ # It's an endpoint object, so put its endpoint conversion
180
+ # attribute into the list of endpoint transformers
181
+ transformers << subfilter.method('from_basic_service_endpoint')
182
+ elsif subfilter.respond_to?('call')
183
+ # It's a proc, so add it to the list of endpoint
184
+ # transformers
185
+ transformers << subfilter
186
+ else
187
+ raise @@filter_type_error
188
+ end
189
+ else
190
+ filters << mk_compound_filter(subfilter)
191
+ end
192
+ }
193
+
194
+ if transformers.length > 0
195
+ filters << TransformFilterMaker.new(transformers)
196
+ end
197
+
198
+ if filters.length == 1
199
+ return filters[0]
200
+ else
201
+ return CompoundFilter.new(filters)
202
+ end
203
+ end
204
+ end
205
+ end
@@ -253,7 +253,7 @@ class HTMLTag < HTMLToken
253
253
  if !@hashed
254
254
  if !@end_tag
255
255
  # Get the attributes
256
- attr_arr = @raw.scan(/<[\w:-]+\s+(.*)>/m)[0]
256
+ attr_arr = @raw.scan(/<[\w:-]+\s+(.*?)\/?>/m)[0]
257
257
  if attr_arr.kind_of?(Array)
258
258
  # Attributes found, parse them
259
259
  attrs = attr_arr[0]
@@ -300,56 +300,3 @@ class HTMLTag < HTMLToken
300
300
  end
301
301
  end
302
302
 
303
- if $0 == __FILE__
304
- require 'test/unit'
305
-
306
- class TC_TestHTMLTokenizer < Test::Unit::TestCase
307
- def test_bad_link
308
- toke = HTMLTokenizer.new("<p><a href=http://bad.com/link>foo</a></p>")
309
- assert("http://bad.com/link" == toke.getTag("a").attr_hash['href'])
310
- end
311
-
312
- def test_namespace
313
- toke = HTMLTokenizer.new("<f:table xmlns:f=\"http://www.com/foo\">")
314
- assert("http://www.com/foo" == toke.getTag("f:table").attr_hash['xmlns:f'])
315
- end
316
-
317
- def test_comment
318
- toke = HTMLTokenizer.new("<!-- comment on me -->")
319
- t = toke.getNextToken
320
- assert(HTMLComment == t.class)
321
- assert("comment on me" == t.contents)
322
- end
323
-
324
-
325
- def test_full
326
- page = "<HTML>
327
- <HEAD>
328
- <TITLE>This is the title</TITLE>
329
- </HEAD>
330
- <!-- Here comes the <a href=\"missing.link\">blah</a>
331
- comment body
332
- -->
333
- <BODY>
334
- <H1>This is the header</H1>
335
- <P>
336
- This is the paragraph, it contains
337
- <a href=\"link.html\">links</a>,
338
- <img src=\"blah.gif\" optional alt='images
339
- are
340
- really cool'>. Ok, here is some more text and
341
- <A href=\"http://another.link.com/\" target=\"_blank\">another link</A>.
342
- </P>
343
- </body>
344
- </HTML>
345
- "
346
- toke = HTMLTokenizer.new(page)
347
-
348
- assert("<h1>" == toke.getTag("h1", "h2", "h3").to_s.downcase)
349
- assert(HTMLTag.new("<a href=\"link.html\">") == toke.getTag("IMG", "A"))
350
- assert("links" == toke.getTrimmedText)
351
- assert(toke.getTag("IMG", "A").attr_hash['optional'])
352
- assert("_blank" == toke.getTag("IMG", "A").attr_hash['target'])
353
- end
354
- end
355
- end
@@ -0,0 +1,36 @@
1
+ require "openid/yadis/htmltokenizer"
2
+ require 'cgi'
3
+
4
+ module OpenID
5
+ module Yadis
6
+ def Yadis.html_yadis_location(html)
7
+ parser = HTMLTokenizer.new(html)
8
+
9
+ # to keep track of whether or not we are in the head element
10
+ in_head = false
11
+
12
+ while el = parser.getTag('head', '/head', 'meta', 'body', '/body', 'html')
13
+
14
+ # we are leaving head or have reached body, so we bail
15
+ return nil if ['/head', 'body', '/body'].member?(el.tag_name)
16
+
17
+ if el.tag_name == 'head'
18
+ unless el.to_s[-2] == 47 # tag ends with a /: a short tag
19
+ in_head = true
20
+ end
21
+ end
22
+ next unless in_head
23
+
24
+ return nil if el.tag_name == 'html'
25
+
26
+ if el.tag_name == 'meta' and (equiv = el.attr_hash['http-equiv'])
27
+ if ['x-xrds-location','x-yadis-location'].member?(equiv.downcase)
28
+ return CGI::unescapeHTML(el.attr_hash['content'])
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,42 @@
1
+
2
+ require 'openid/yadis/filters'
3
+ require 'openid/yadis/discovery'
4
+ require 'openid/yadis/xrds'
5
+
6
+ module OpenID
7
+ module Yadis
8
+ def Yadis.get_service_endpoints(input_url, flt=nil)
9
+ # Perform the Yadis protocol on the input URL and return an
10
+ # iterable of resulting endpoint objects.
11
+ #
12
+ # @param flt: A filter object or something that is convertable
13
+ # to a filter object (using mkFilter) that will be used to
14
+ # generate endpoint objects. This defaults to generating
15
+ # BasicEndpoint objects.
16
+ result = Yadis.discover(input_url)
17
+ begin
18
+ endpoints = Yadis.apply_filter(result.normalized_uri,
19
+ result.response_text, flt)
20
+ rescue XRDSError => err
21
+ raise DiscoveryFailure.new(err.to_s, nil)
22
+ end
23
+
24
+ return [result.normalized_uri, endpoints]
25
+ end
26
+
27
+ def Yadis.apply_filter(normalized_uri, xrd_data, flt=nil)
28
+ # Generate an iterable of endpoint objects given this input data,
29
+ # presumably from the result of performing the Yadis protocol.
30
+
31
+ flt = Yadis.make_filter(flt)
32
+ et = Yadis.parseXRDS(xrd_data)
33
+
34
+ endpoints = []
35
+ each_service(et) { |service_element|
36
+ endpoints += flt.get_service_endpoints(normalized_uri, service_element)
37
+ }
38
+
39
+ return endpoints
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,171 @@
1
+ require 'rexml/document'
2
+ require 'rexml/element'
3
+ require 'rexml/xpath'
4
+
5
+ module OpenID
6
+ module Yadis
7
+
8
+ XRD_NS_2_0 = 'xri://$xrd*($v*2.0)'
9
+ XRDS_NS = 'xri://$xrds'
10
+
11
+ XRDS_NAMESPACES = {
12
+ 'xrds' => XRDS_NS,
13
+ 'xrd' => XRD_NS_2_0,
14
+ }
15
+
16
+ class XRDSError < StandardError; end
17
+
18
+ # Raised when there's an assertion in the XRDS that it does not
19
+ # have the authority to make.
20
+ class XRDSFraud < XRDSError
21
+ end
22
+
23
+ def Yadis::get_canonical_id(iname, xrd_tree)
24
+ # Return the CanonicalID from this XRDS document.
25
+ #
26
+ # @param iname: the XRI being resolved.
27
+ # @type iname: unicode
28
+ #
29
+ # @param xrd_tree: The XRDS output from the resolver.
30
+ #
31
+ # @returns: The XRI CanonicalID or None.
32
+ # @returntype: unicode or None
33
+
34
+ xrd_list = []
35
+ REXML::XPath::match(xrd_tree.root, '/xrds:XRDS/xrd:XRD', XRDS_NAMESPACES).each { |el|
36
+ xrd_list << el
37
+ }
38
+
39
+ xrd_list.reverse!
40
+
41
+ cid_elements = []
42
+
43
+ if !xrd_list.empty?
44
+ xrd_list[0].elements.each { |e|
45
+ if !e.respond_to?('name')
46
+ next
47
+ end
48
+ if e.name == 'CanonicalID'
49
+ cid_elements << e
50
+ end
51
+ }
52
+ end
53
+
54
+ cid_element = cid_elements[-1]
55
+
56
+ if !cid_element
57
+ return nil
58
+ end
59
+
60
+ canonicalID = XRI.make_xri(cid_element.text)
61
+
62
+ childID = canonicalID
63
+
64
+ xrd_list[1..-1].each { |xrd|
65
+ parent_sought = childID[0...childID.rindex('!')]
66
+
67
+ parent_list = []
68
+ xrd.elements.each_element(CANONICALID_TAG) { |c|
69
+ XRI.make_xri(c.text)
70
+ }
71
+
72
+ if !parent_list.member?(parent_sought)
73
+ raise XRDSFraud.new(sprintf("%s can not come from any of %s", parent_sought,
74
+ parent_list))
75
+ end
76
+
77
+ childID = parent_sought
78
+ }
79
+
80
+ root = XRI.root_authority(iname)
81
+ if not XRI.provider_is_authoritative(root, childID)
82
+ raise XRDSFraud.new(sprintf("%s can not come from root %s", childID, root))
83
+ end
84
+
85
+ return canonicalID
86
+ end
87
+
88
+ def Yadis::mkXRDSTag(name)
89
+ e = REXML::Element.new('xrds:' + name)
90
+ e.add_namespace('xrds', XRDS_NS)
91
+ return e
92
+ end
93
+
94
+ def Yadis::mkXRDTag(name)
95
+ e = REXML::Element.new('xrd:' + name)
96
+ e.add_namespace('xrd', XRD_NS_2_0)
97
+ return e
98
+ end
99
+
100
+ ROOT_TAG = Yadis::mkXRDSTag('XRDS')
101
+ CANONICALID_TAG = mkXRDTag('CanonicalID')
102
+
103
+ class XRDSError < StandardError
104
+ end
105
+
106
+ def Yadis::parseXRDS(text)
107
+ if text.nil?
108
+ raise XRDSError.new("Not an XRDS document.")
109
+ end
110
+
111
+ begin
112
+ d = REXML::Document.new(text)
113
+ rescue RuntimeError => why
114
+ raise XRDSError.new("Not an XRDS document. Failed to parse XML.")
115
+ end
116
+
117
+ if is_xrds?(d)
118
+ return d
119
+ else
120
+ raise XRDSError.new("Not an XRDS document.")
121
+ end
122
+ end
123
+
124
+ def Yadis::is_xrds?(xrds_tree)
125
+ xrds_root = xrds_tree.root
126
+ return (!xrds_root.nil? and
127
+ xrds_root.name == ROOT_TAG.name and
128
+ xrds_root.namespace == ROOT_TAG.namespace)
129
+ end
130
+
131
+ def Yadis::get_yadis_xrd(xrds_tree)
132
+ REXML::XPath.each(xrds_tree.root,
133
+ '/xrds:XRDS/xrd:XRD[last()]',
134
+ XRDS_NAMESPACES) { |el|
135
+ return el
136
+ }
137
+ raise XRDSError.new("No XRD element found.")
138
+ end
139
+
140
+ # aka iterServices in Python
141
+ def Yadis::each_service(xrds_tree, &block)
142
+ xrd = get_yadis_xrd(xrds_tree)
143
+ xrd.each_element('Service', &block)
144
+ end
145
+
146
+ def Yadis::services(xrds_tree)
147
+ s = []
148
+ each_service(xrds_tree) { |service|
149
+ s << service
150
+ }
151
+ return s
152
+ end
153
+
154
+ def Yadis::expand_service(service_element)
155
+ es = service_element.elements
156
+ uris = es.each('URI') { |u| }
157
+ uris = prio_sort(uris)
158
+ types = es.each('Type/text()')
159
+ # REXML::Text objects are not strings.
160
+ types = types.collect { |t| t.to_s }
161
+ uris.collect { |uri| [types, uri.text, service_element] }
162
+ end
163
+
164
+ # Sort a list of elements that have priority attributes.
165
+ def Yadis::prio_sort(elements)
166
+ elements.sort { |a,b|
167
+ a.attribute('priority').to_s.to_i <=> b.attribute('priority').to_s.to_i
168
+ }
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,90 @@
1
+ require 'openid/yadis/xrds'
2
+ require 'openid/fetchers'
3
+
4
+ module OpenID
5
+ module Yadis
6
+ module XRI
7
+
8
+ # The '(' is for cross-reference authorities, and hopefully has a
9
+ # matching ')' somewhere.
10
+ XRI_AUTHORITIES = ["!", "=", "@", "+", "$", "("]
11
+
12
+ def self.identifier_scheme(identifier)
13
+ if (!identifier.nil? and
14
+ identifier.length > 0 and
15
+ (identifier.match('^xri://') or
16
+ XRI_AUTHORITIES.member?(identifier[0].chr)))
17
+ return :xri
18
+ else
19
+ return :uri
20
+ end
21
+ end
22
+
23
+ # Transform an XRI reference to an IRI reference. Note this is
24
+ # not not idempotent, so do not apply this to an identifier more
25
+ # than once. XRI Syntax section 2.3.1
26
+ def self.to_iri_normal(xri)
27
+ iri = xri.dup
28
+ iri.insert(0, 'xri://') if not iri.match('^xri://')
29
+ return escape_for_iri(iri)
30
+ end
31
+
32
+ # Note this is not not idempotent, so do not apply this more than
33
+ # once. XRI Syntax section 2.3.2
34
+ def self.escape_for_iri(xri)
35
+ esc = xri.dup
36
+ # encode all %
37
+ esc.gsub!(/%/, '%25')
38
+ esc.gsub!(/\((.*?)\)/) { |xref_match|
39
+ xref_match.gsub(/[\/\?\#]/) { |char_match|
40
+ CGI::escape(char_match)
41
+ }
42
+ }
43
+ return esc
44
+ end
45
+
46
+ # Transform an XRI reference to a URI reference. Note this is not
47
+ # not idempotent, so do not apply this to an identifier more than
48
+ # once. XRI Syntax section 2.3.1
49
+ def self.to_uri_normal(xri)
50
+ return iri_to_uri(to_iri_normal(xri))
51
+ end
52
+
53
+ # RFC 3987 section 3.1
54
+ def self.iri_to_uri(iri)
55
+ uri = iri.dup
56
+ # for char in ucschar or iprivate
57
+ # convert each char to %HH%HH%HH (as many %HH as octets)
58
+ return uri
59
+ end
60
+
61
+ def self.provider_is_authoritative(provider_id, canonical_id)
62
+ lastbang = canonical_id.rindex('!')
63
+ return false unless lastbang
64
+ parent = canonical_id[0...lastbang]
65
+ return parent == provider_id
66
+ end
67
+
68
+ def self.root_authority(xri)
69
+ xri = xri[6..-1] if xri.index('xri://') == 0
70
+ authority = xri.split('/', 2)[0]
71
+ if authority[0].chr == '('
72
+ root = authority[0...authority.index(')')+1]
73
+ elsif XRI_AUTHORITIES.member?(authority[0].chr)
74
+ root = authority[0].chr
75
+ else
76
+ root = authority.split(/[!*]/)[0]
77
+ end
78
+
79
+ self.make_xri(root)
80
+ end
81
+
82
+ def self.make_xri(xri)
83
+ if xri.index('xri://') != 0
84
+ xri = 'xri://' + xri
85
+ end
86
+ return xri
87
+ end
88
+ end
89
+ end
90
+ end