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
@@ -1,114 +1,341 @@
1
1
  require 'uri'
2
-
3
- TOP_LEVEL_DOMAINS = 'com|edu|gov|int|mil|net|org|biz|info|name|museum|coop|aero|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|fi|fj|fk|fm|fo|fr|ga|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw'.split('|')
2
+ require 'openid/urinorm'
4
3
 
5
4
  module OpenID
6
5
 
7
- class TrustRoot
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
8
18
 
9
- @@empty_re = Regexp.new('^http[s]*:\/\/\*\/$')
19
+ module TrustRoot
20
+ TOP_LEVEL_DOMAINS = %w'
21
+ com edu gov int mil net org biz info name museum coop aero ac ad
22
+ ae af ag ai al am an ao aq ar as at au aw az ba bb bd be bf bg
23
+ bh bi bj bm bn bo br bs bt bv bw by bz ca cc cd cf cg ch ci ck
24
+ cl cm cn co cr cu cv cx cy cz de dj dk dm do dz ec ee eg eh er
25
+ es et eu fi fj fk fm fo fr ga gd ge gf gg gh gi gl gm gn gp gq
26
+ gr gs gt gu gw gy hk hm hn hr ht hu id ie il im in io iq ir is
27
+ it je jm jo jp ke kg kh ki km kn kp kr kw ky kz la lb lc li lk
28
+ lr ls lt lu lv ly ma mc md mg mh mk ml mm mn mo mp mq mr ms mt
29
+ mu mv mw mx my mz na nc ne nf ng ni nl no np nr nu nz om pa pe
30
+ pf pg ph pk pl pm pn pr ps pt pw py qa re ro ru rw sa sb sc sd
31
+ se sg sh si sj sk sl sm sn so sr st sv sy sz tc td tf tg th tj
32
+ tk tm tn to tp tr tt tv tw tz ua ug uk um us uy uz va vc ve vg
33
+ vi vn vu wf ws ye yt yu za zm zw'
10
34
 
11
- def TrustRoot._parse_url(url)
12
- begin
13
- parsed = URI::parse(url)
14
- rescue
35
+ ALLOWED_PROTOCOLS = ['http', 'https']
36
+
37
+ # The URI for relying party discovery, used in realm verification.
38
+ #
39
+ # XXX: This should probably live somewhere else (like in
40
+ # OpenID or OpenID::Yadis somewhere)
41
+ RP_RETURN_TO_URL_TYPE = 'http://specs.openid.net/auth/2.0/return_to'
42
+
43
+ # If the endpoint is a relying party OpenID return_to endpoint,
44
+ # return the endpoint URL. Otherwise, return None.
45
+ #
46
+ # This function is intended to be used as a filter for the Yadis
47
+ # filtering interface.
48
+ #
49
+ # endpoint: An XRDS BasicServiceEndpoint, as returned by
50
+ # performing Yadis dicovery.
51
+ #
52
+ # returns the endpoint URL or None if the endpoint is not a
53
+ # relying party endpoint.
54
+ def TrustRoot._extract_return_url(endpoint)
55
+ if endpoint.matchTypes([RP_RETURN_TO_URL_TYPE])
56
+ return endpoint.uri
57
+ else
15
58
  return nil
16
59
  end
17
-
18
- return [parsed.scheme, parsed.host, parsed.port, parsed.path]
19
60
  end
20
61
 
21
- def TrustRoot.parse(trust_root)
22
- return nil unless trust_root.instance_of?(String)
62
+ # Is the return_to URL under one of the supplied allowed
63
+ # return_to URLs?
64
+ def TrustRoot.return_to_matches(allowed_return_to_urls, return_to)
65
+ allowed_return_to_urls.each { |allowed_return_to|
66
+ # A return_to pattern works the same as a realm, except that
67
+ # it's not allowed to use a wildcard. We'll model this by
68
+ # parsing it as a realm, and not trying to match it if it has
69
+ # a wildcard.
23
70
 
24
- trust_root = trust_root.dup
25
- unparsed = trust_root.dup
71
+ return_realm = TrustRoot.parse(allowed_return_to)
72
+ if (# Parses as a trust root
73
+ !return_realm.nil? and
26
74
 
27
- # look for wildcard
28
- wildcard = (not trust_root.index('://*.').nil?)
29
- trust_root.sub!('*.', '') if wildcard
30
-
31
- # handle http://*/ case
32
- if not wildcard and @@empty_re.match(trust_root)
33
- proto = trust_root.split(':')[0]
34
- port = proto == 'http' ? 80 : 443
35
- return new(unparsed, proto, true, '', port, '/')
36
- end
75
+ # Does not have a wildcard
76
+ !return_realm.wildcard and
37
77
 
38
- parts = TrustRoot._parse_url(trust_root)
39
- return nil if parts.nil?
78
+ # Matches the return_to that we passed in with it
79
+ return_realm.validate_url(return_to)
80
+ )
81
+ return true
82
+ end
83
+ }
40
84
 
41
- proto, host, port, path = parts
42
-
43
- return nil unless ['http', 'https'].member?(proto)
44
- return new(unparsed, proto, wildcard, host, port, path)
85
+ # No URL in the list matched
86
+ return false
45
87
  end
46
88
 
47
- def TrustRoot.check_sanity(trust_root)
48
- tr = TrustRoot.parse(trust_root)
49
- return false if tr.nil?
50
- return tr.sane?
51
- end
52
-
53
-
54
- def initialize(unparsed, proto, wildcard, host, port, path)
55
- @unparsed = unparsed
56
- @proto = proto
57
- @wildcard = wildcard
58
- @host = host
59
- @port = port
60
- @path = path
61
- end
89
+ # Given a relying party discovery URL return a list of return_to
90
+ # URLs.
91
+ def TrustRoot.get_allowed_return_urls(relying_party_url)
92
+ rp_url_after_redirects, return_to_urls = services.get_service_endpoints(
93
+ relying_party_url, _extract_return_url)
62
94
 
63
- def sane?
64
- return true if @host == 'localhost'
65
-
66
- host_parts = @host.split('.')
67
- # a note: ruby string split does not put an empty string
68
- # at the end of the list if the split element is last. for example,
69
- # 'foo.com.'.split('.') => ['foo','com']. Mentioned because the python
70
- # code differs here.
71
-
72
- return false if host_parts.length == 0
73
-
74
- # no adjacent dots
75
- return false if host_parts.member?('')
76
-
77
- # last part must be a tld
78
- tld = host_parts[-1]
79
- return false unless TOP_LEVEL_DOMAINS.member?(tld)
80
-
81
- return false if host_parts.length == 1
82
-
83
- if @wildcard
84
- if tld.length == 2 and host_parts[-2].length <= 3
85
- # It's a 2-letter tld with a short second to last segment
86
- # so there needs to be more than two segments specified
87
- # (e.g. *.co.uk is insane)
88
- return host_parts.length > 2
89
- end
95
+ if rp_url_after_redirects != relying_party_url
96
+ # Verification caused a redirect
97
+ raise RealmVerificationRedirected.new(
98
+ relying_party_url, rp_url_after_redirects)
90
99
  end
91
100
 
92
- return true
101
+ return return_to_urls
93
102
  end
94
-
95
- def validate_url(url)
96
- parts = TrustRoot._parse_url(url)
97
- return false if parts.nil?
98
-
99
- proto, host, port, path = parts
100
-
101
- return false unless proto == @proto
102
- return false unless port == @port
103
- return false unless path.index(@path) == 0
104
-
105
- if @wildcard
106
- return (not host.rindex(@host).nil?)
103
+
104
+ # Verify that a return_to URL is valid for the given realm.
105
+ #
106
+ # This function builds a discovery URL, performs Yadis discovery
107
+ # on it, makes sure that the URL does not redirect, parses out
108
+ # the return_to URLs, and finally checks to see if the current
109
+ # return_to URL matches the return_to.
110
+ #
111
+ # raises DiscoveryFailure when Yadis discovery fails returns
112
+ # true if the return_to URL is valid for the realm
113
+ def TrustRoot.verify_return_to(realm_str, return_to, _vrfy=nil)
114
+ # _vrfy parameter is there to make testing easier
115
+ if _vrfy.nil?
116
+ _vrfy = self.method('get_allowed_return_urls')
117
+ end
118
+
119
+ if !(_vrfy.is_a?(Proc) or _vrfy.is_a?(Method))
120
+ raise ArgumentError, "_vrfy must be a Proc or Method"
121
+ end
122
+
123
+ realm = TrustRoot.parse(realm_str)
124
+ if realm.nil?
125
+ # The realm does not parse as a URL pattern
126
+ return false
127
+ end
128
+
129
+ begin
130
+ allowable_urls = _vrfy.call(realm.build_discovery_url())
131
+ rescue RealmVerificationRedirected => err
132
+ Util.log(err.to_s)
133
+ return false
134
+ end
135
+
136
+ if return_to_matches(allowable_urls, return_to)
137
+ return true
107
138
  else
108
- return (host == @host)
139
+ Util.log("Failed to validate return_to #{return_to} for " +
140
+ "realm #{realm_str}, was not in #{allowable_urls}")
141
+ return false
109
142
  end
110
143
  end
111
144
 
145
+ class TrustRoot
146
+
147
+ attr_reader :unparsed, :proto, :wildcard, :host, :port, :path
148
+
149
+ @@empty_re = Regexp.new('^http[s]*:\/\/\*\/$')
150
+
151
+ def TrustRoot._build_path(path, query=nil, frag=nil)
152
+ s = path.dup
153
+
154
+ frag = nil if frag == ''
155
+ query = nil if query == ''
156
+
157
+ if query
158
+ s << "?" << query
159
+ end
160
+
161
+ if frag
162
+ s << "#" << frag
163
+ end
164
+
165
+ return s
166
+ end
167
+
168
+ def TrustRoot._parse_url(url)
169
+ begin
170
+ url = URINorm.urinorm(url)
171
+ rescue URI::InvalidURIError => err
172
+ nil
173
+ end
174
+
175
+ begin
176
+ parsed = URI::parse(url)
177
+ rescue URI::InvalidURIError
178
+ return nil
179
+ end
180
+
181
+ path = TrustRoot._build_path(parsed.path,
182
+ parsed.query,
183
+ parsed.fragment)
184
+
185
+ return [parsed.scheme || '', parsed.host || '',
186
+ parsed.port || '', path || '']
187
+ end
188
+
189
+ def TrustRoot.parse(trust_root)
190
+ return nil unless trust_root.instance_of?(String)
191
+
192
+ trust_root = trust_root.dup
193
+ unparsed = trust_root.dup
194
+
195
+ # look for wildcard
196
+ wildcard = (not trust_root.index('://*.').nil?)
197
+ trust_root.sub!('*.', '') if wildcard
198
+
199
+ # handle http://*/ case
200
+ if not wildcard and @@empty_re.match(trust_root)
201
+ proto = trust_root.split(':')[0]
202
+ port = proto == 'http' ? 80 : 443
203
+ return new(unparsed, proto, true, '', port, '/')
204
+ end
205
+
206
+ parts = TrustRoot._parse_url(trust_root)
207
+ return nil if parts.nil?
208
+
209
+ proto, host, port, path = parts
210
+
211
+ # check for URI fragment
212
+ if path and !path.index('#').nil?
213
+ return nil
214
+ end
215
+
216
+ return nil unless ['http', 'https'].member?(proto)
217
+ return new(unparsed, proto, wildcard, host, port, path)
218
+ end
219
+
220
+ def TrustRoot.check_sanity(trust_root)
221
+ return TrustRoot.parse(trust_root).sane?
222
+ end
223
+
224
+ # quick func for validating a url against a trust root. See the
225
+ # TrustRoot class if you need more control.
226
+ def self.check_url(trust_root, url)
227
+ tr = self.parse(trust_root)
228
+ return (!tr.nil? and tr.validate_url(url))
229
+ end
230
+
231
+ # Return a discovery URL for this realm.
232
+ #
233
+ # This function does not check to make sure that the realm is
234
+ # valid. Its behaviour on invalid inputs is undefined.
235
+ #
236
+ # return_to:: The relying party return URL of the OpenID
237
+ # authentication request
238
+ #
239
+ # Returns the URL upon which relying party discovery should be
240
+ # run in order to verify the return_to URL
241
+ def build_discovery_url
242
+ if self.wildcard
243
+ # Use "www." in place of the star
244
+ www_domain = 'www.' + @host
245
+ port = (!@port.nil? and ![80, 443].member?(@port)) ? (":" + @port.to_s) : ''
246
+ return "#{@proto}://#{www_domain}#{port}#{@path}"
247
+ else
248
+ return @unparsed
249
+ end
250
+ end
251
+
252
+ def initialize(unparsed, proto, wildcard, host, port, path)
253
+ @unparsed = unparsed
254
+ @proto = proto
255
+ @wildcard = wildcard
256
+ @host = host
257
+ @port = port
258
+ @path = path
259
+ end
260
+
261
+ def sane?
262
+ return true if @host == 'localhost'
263
+
264
+ host_parts = @host.split('.')
265
+
266
+ # a note: ruby string split does not put an empty string at
267
+ # the end of the list if the split element is last. for
268
+ # example, 'foo.com.'.split('.') => ['foo','com']. Mentioned
269
+ # because the python code differs here.
270
+
271
+ return false if host_parts.length == 0
272
+
273
+ # no adjacent dots
274
+ return false if host_parts.member?('')
275
+
276
+ # last part must be a tld
277
+ tld = host_parts[-1]
278
+ return false unless TOP_LEVEL_DOMAINS.member?(tld)
279
+
280
+ return false if host_parts.length == 1
281
+
282
+ if @wildcard
283
+ if tld.length == 2 and host_parts[-2].length <= 3
284
+ # It's a 2-letter tld with a short second to last segment
285
+ # so there needs to be more than two segments specified
286
+ # (e.g. *.co.uk is insane)
287
+ return host_parts.length > 2
288
+ end
289
+ end
290
+
291
+ return true
292
+ end
293
+
294
+ def validate_url(url)
295
+ parts = TrustRoot._parse_url(url)
296
+ return false if parts.nil?
297
+
298
+ proto, host, port, path = parts
299
+
300
+ return false unless proto == @proto
301
+ return false unless port == @port
302
+ return false unless host.index('*').nil?
303
+
304
+ if !@wildcard
305
+ if host != @host
306
+ return false
307
+ end
308
+ elsif ((@host != '') and
309
+ (!host.ends_with?('.' + @host)) and
310
+ (host != @host))
311
+ return false
312
+ end
313
+
314
+ if path != @path
315
+ path_len = @path.length
316
+ trust_prefix = @path[0...path_len]
317
+ url_prefix = path[0...path_len]
318
+
319
+ # must be equal up to the length of the path, at least
320
+ if trust_prefix != url_prefix
321
+ return false
322
+ end
323
+
324
+ # These characters must be on the boundary between the end
325
+ # of the trust root's path and the start of the URL's path.
326
+ if !@path.index('?').nil?
327
+ allowed = '&'
328
+ else
329
+ allowed = '?/'
330
+ end
331
+
332
+ return (!allowed.index(@path[-1]).nil? or
333
+ !allowed.index(path[path_len]).nil?)
334
+ end
335
+
336
+ return true
337
+ end
338
+ end
112
339
  end
113
340
  end
114
341
 
@@ -1,16 +1,44 @@
1
1
  require 'uri'
2
2
 
3
- UNRESERVED = [false]*256
4
- ('A'[0]..'Z'[0]).each {|i| UNRESERVED[i] = true}
5
- ('a'[0]..'z'[0]).each {|i| UNRESERVED[i] = true}
6
- ('0'[0]..'9'[0]).each {|i| UNRESERVED[i] = true}
7
- %W(- . _ ~).each {|i| UNRESERVED[i[0]] = true}
3
+ require "openid/extras"
8
4
 
9
5
  module OpenID
10
6
 
11
- module Util
12
-
13
- def Util._remove_dot_segments(path)
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)
14
42
  result_segments = []
15
43
 
16
44
  while path.length > 0
@@ -39,34 +67,9 @@ module OpenID
39
67
  path = path[i..-1]
40
68
  end
41
69
  end
42
-
43
- return result_segments.join('')
44
- end
45
-
46
- def Util.urinorm(uri)
47
- uri = URI.parse(uri)
48
-
49
- raise URI::InvalidURIError.new('no scheme') unless uri.scheme
50
- uri.scheme = uri.scheme.downcase
51
- unless ['http','https'].member?(uri.scheme)
52
- raise URI::InvalidURIError.new('Not an HTTP or HTTPS URI')
53
- end
54
70
 
55
- raise URI::InvalidURIError.new('no host') unless uri.host
56
- uri.host = uri.host.downcase
57
-
58
- uri.path = _remove_dot_segments(uri.path)
59
- uri.path = '/' if uri.path.length == 0
60
-
61
- uri = uri.normalize.to_s
62
- uri = uri.gsub(/%[0-9a-zA-Z]{2}/) {
63
- i = $&[1..2].upcase.to_i(16)
64
- UNRESERVED[i] ? i.chr : $&.upcase
65
- }
66
-
67
- return uri
71
+ return result_segments.join('')
68
72
  end
69
-
70
73
  end
71
74
 
72
75
  end