entp-ruby-openid 2.2

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 (200) hide show
  1. data/CHANGELOG +215 -0
  2. data/INSTALL +47 -0
  3. data/LICENSE +210 -0
  4. data/NOTICE +2 -0
  5. data/README +85 -0
  6. data/UPGRADE +127 -0
  7. data/admin/runtests.rb +45 -0
  8. data/examples/README +32 -0
  9. data/examples/active_record_openid_store/README +58 -0
  10. data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +24 -0
  11. data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
  12. data/examples/active_record_openid_store/init.rb +8 -0
  13. data/examples/active_record_openid_store/lib/association.rb +10 -0
  14. data/examples/active_record_openid_store/lib/nonce.rb +3 -0
  15. data/examples/active_record_openid_store/lib/open_id_setting.rb +4 -0
  16. data/examples/active_record_openid_store/lib/openid_ar_store.rb +57 -0
  17. data/examples/active_record_openid_store/test/store_test.rb +212 -0
  18. data/examples/discover +49 -0
  19. data/examples/rails_openid/README +153 -0
  20. data/examples/rails_openid/Rakefile +10 -0
  21. data/examples/rails_openid/app/controllers/application.rb +4 -0
  22. data/examples/rails_openid/app/controllers/consumer_controller.rb +125 -0
  23. data/examples/rails_openid/app/controllers/login_controller.rb +45 -0
  24. data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
  25. data/examples/rails_openid/app/helpers/application_helper.rb +3 -0
  26. data/examples/rails_openid/app/helpers/login_helper.rb +2 -0
  27. data/examples/rails_openid/app/helpers/server_helper.rb +9 -0
  28. data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
  29. data/examples/rails_openid/app/views/layouts/server.rhtml +68 -0
  30. data/examples/rails_openid/app/views/login/index.rhtml +56 -0
  31. data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
  32. data/examples/rails_openid/config/boot.rb +19 -0
  33. data/examples/rails_openid/config/database.yml +74 -0
  34. data/examples/rails_openid/config/environment.rb +54 -0
  35. data/examples/rails_openid/config/environments/development.rb +19 -0
  36. data/examples/rails_openid/config/environments/production.rb +19 -0
  37. data/examples/rails_openid/config/environments/test.rb +19 -0
  38. data/examples/rails_openid/config/routes.rb +24 -0
  39. data/examples/rails_openid/doc/README_FOR_APP +2 -0
  40. data/examples/rails_openid/public/404.html +8 -0
  41. data/examples/rails_openid/public/500.html +8 -0
  42. data/examples/rails_openid/public/dispatch.cgi +12 -0
  43. data/examples/rails_openid/public/dispatch.fcgi +26 -0
  44. data/examples/rails_openid/public/dispatch.rb +12 -0
  45. data/examples/rails_openid/public/favicon.ico +0 -0
  46. data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
  47. data/examples/rails_openid/public/javascripts/controls.js +750 -0
  48. data/examples/rails_openid/public/javascripts/dragdrop.js +584 -0
  49. data/examples/rails_openid/public/javascripts/effects.js +854 -0
  50. data/examples/rails_openid/public/javascripts/prototype.js +1785 -0
  51. data/examples/rails_openid/public/robots.txt +1 -0
  52. data/examples/rails_openid/script/about +3 -0
  53. data/examples/rails_openid/script/breakpointer +3 -0
  54. data/examples/rails_openid/script/console +3 -0
  55. data/examples/rails_openid/script/destroy +3 -0
  56. data/examples/rails_openid/script/generate +3 -0
  57. data/examples/rails_openid/script/performance/benchmarker +3 -0
  58. data/examples/rails_openid/script/performance/profiler +3 -0
  59. data/examples/rails_openid/script/plugin +3 -0
  60. data/examples/rails_openid/script/process/reaper +3 -0
  61. data/examples/rails_openid/script/process/spawner +3 -0
  62. data/examples/rails_openid/script/process/spinner +3 -0
  63. data/examples/rails_openid/script/runner +3 -0
  64. data/examples/rails_openid/script/server +3 -0
  65. data/examples/rails_openid/test/functional/login_controller_test.rb +18 -0
  66. data/examples/rails_openid/test/functional/server_controller_test.rb +18 -0
  67. data/examples/rails_openid/test/test_helper.rb +28 -0
  68. data/lib/hmac/hmac.rb +112 -0
  69. data/lib/hmac/sha1.rb +11 -0
  70. data/lib/hmac/sha2.rb +25 -0
  71. data/lib/openid.rb +22 -0
  72. data/lib/openid/association.rb +249 -0
  73. data/lib/openid/consumer.rb +395 -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 +497 -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 +150 -0
  81. data/lib/openid/cryptutil.rb +115 -0
  82. data/lib/openid/dh.rb +89 -0
  83. data/lib/openid/extension.rb +39 -0
  84. data/lib/openid/extensions/ax.rb +539 -0
  85. data/lib/openid/extensions/oauth.rb +91 -0
  86. data/lib/openid/extensions/pape.rb +179 -0
  87. data/lib/openid/extensions/sreg.rb +277 -0
  88. data/lib/openid/extras.rb +11 -0
  89. data/lib/openid/fetchers.rb +258 -0
  90. data/lib/openid/kvform.rb +136 -0
  91. data/lib/openid/kvpost.rb +58 -0
  92. data/lib/openid/message.rb +553 -0
  93. data/lib/openid/protocolerror.rb +12 -0
  94. data/lib/openid/server.rb +1544 -0
  95. data/lib/openid/store.rb +10 -0
  96. data/lib/openid/store/filesystem.rb +272 -0
  97. data/lib/openid/store/interface.rb +75 -0
  98. data/lib/openid/store/memcache.rb +109 -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 +119 -0
  104. data/lib/openid/version.rb +3 -0
  105. data/lib/openid/yadis.rb +15 -0
  106. data/lib/openid/yadis/accept.rb +148 -0
  107. data/lib/openid/yadis/constants.rb +21 -0
  108. data/lib/openid/yadis/discovery.rb +153 -0
  109. data/lib/openid/yadis/filters.rb +205 -0
  110. data/lib/openid/yadis/htmltokenizer.rb +305 -0
  111. data/lib/openid/yadis/parsehtml.rb +45 -0
  112. data/lib/openid/yadis/services.rb +42 -0
  113. data/lib/openid/yadis/xrds.rb +155 -0
  114. data/lib/openid/yadis/xri.rb +90 -0
  115. data/lib/openid/yadis/xrires.rb +91 -0
  116. data/test/data/test_discover/openid_utf8.html +11 -0
  117. data/test/support/test_data_mixin.rb +127 -0
  118. data/test/support/test_util.rb +53 -0
  119. data/test/support/yadis_data.rb +131 -0
  120. data/test/support/yadis_data/accept.txt +124 -0
  121. data/test/support/yadis_data/dh.txt +29 -0
  122. data/test/support/yadis_data/example-xrds.xml +14 -0
  123. data/test/support/yadis_data/linkparse.txt +587 -0
  124. data/test/support/yadis_data/n2b64 +650 -0
  125. data/test/support/yadis_data/test1-discover.txt +137 -0
  126. data/test/support/yadis_data/test1-parsehtml.txt +152 -0
  127. data/test/support/yadis_data/test_discover/malformed_meta_tag.html +19 -0
  128. data/test/support/yadis_data/test_discover/openid.html +11 -0
  129. data/test/support/yadis_data/test_discover/openid2.html +11 -0
  130. data/test/support/yadis_data/test_discover/openid2_xrds.xml +12 -0
  131. data/test/support/yadis_data/test_discover/openid2_xrds_no_local_id.xml +11 -0
  132. data/test/support/yadis_data/test_discover/openid_1_and_2.html +11 -0
  133. data/test/support/yadis_data/test_discover/openid_1_and_2_xrds.xml +16 -0
  134. data/test/support/yadis_data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
  135. data/test/support/yadis_data/test_discover/openid_and_yadis.html +12 -0
  136. data/test/support/yadis_data/test_discover/openid_no_delegate.html +10 -0
  137. data/test/support/yadis_data/test_discover/openid_utf8.html +11 -0
  138. data/test/support/yadis_data/test_discover/yadis_0entries.xml +12 -0
  139. data/test/support/yadis_data/test_discover/yadis_2_bad_local_id.xml +15 -0
  140. data/test/support/yadis_data/test_discover/yadis_2entries_delegate.xml +22 -0
  141. data/test/support/yadis_data/test_discover/yadis_2entries_idp.xml +21 -0
  142. data/test/support/yadis_data/test_discover/yadis_another_delegate.xml +14 -0
  143. data/test/support/yadis_data/test_discover/yadis_idp.xml +12 -0
  144. data/test/support/yadis_data/test_discover/yadis_idp_delegate.xml +13 -0
  145. data/test/support/yadis_data/test_discover/yadis_no_delegate.xml +11 -0
  146. data/test/support/yadis_data/test_xrds/=j3h.2007.11.14.xrds +25 -0
  147. data/test/support/yadis_data/test_xrds/README +12 -0
  148. data/test/support/yadis_data/test_xrds/delegated-20060809-r1.xrds +34 -0
  149. data/test/support/yadis_data/test_xrds/delegated-20060809-r2.xrds +34 -0
  150. data/test/support/yadis_data/test_xrds/delegated-20060809.xrds +34 -0
  151. data/test/support/yadis_data/test_xrds/no-xrd.xml +7 -0
  152. data/test/support/yadis_data/test_xrds/not-xrds.xml +2 -0
  153. data/test/support/yadis_data/test_xrds/prefixsometimes.xrds +34 -0
  154. data/test/support/yadis_data/test_xrds/ref.xrds +109 -0
  155. data/test/support/yadis_data/test_xrds/sometimesprefix.xrds +34 -0
  156. data/test/support/yadis_data/test_xrds/spoof1.xrds +25 -0
  157. data/test/support/yadis_data/test_xrds/spoof2.xrds +25 -0
  158. data/test/support/yadis_data/test_xrds/spoof3.xrds +37 -0
  159. data/test/support/yadis_data/test_xrds/status222.xrds +9 -0
  160. data/test/support/yadis_data/test_xrds/subsegments.xrds +58 -0
  161. data/test/support/yadis_data/test_xrds/valid-populated-xrds.xml +39 -0
  162. data/test/support/yadis_data/trustroot.txt +153 -0
  163. data/test/support/yadis_data/urinorm.txt +79 -0
  164. data/test/test_accept.rb +170 -0
  165. data/test/test_association.rb +268 -0
  166. data/test/test_associationmanager.rb +918 -0
  167. data/test/test_ax.rb +690 -0
  168. data/test/test_checkid_request.rb +293 -0
  169. data/test/test_consumer.rb +260 -0
  170. data/test/test_cryptutil.rb +119 -0
  171. data/test/test_dh.rb +85 -0
  172. data/test/test_discover.rb +848 -0
  173. data/test/test_discovery_manager.rb +259 -0
  174. data/test/test_extension.rb +46 -0
  175. data/test/test_extras.rb +35 -0
  176. data/test/test_fetchers.rb +554 -0
  177. data/test/test_filters.rb +269 -0
  178. data/test/test_helper.rb +4 -0
  179. data/test/test_idres.rb +961 -0
  180. data/test/test_kvform.rb +164 -0
  181. data/test/test_kvpost.rb +64 -0
  182. data/test/test_linkparse.rb +100 -0
  183. data/test/test_message.rb +1115 -0
  184. data/test/test_nonce.rb +89 -0
  185. data/test/test_oauth.rb +176 -0
  186. data/test/test_openid_yadis.rb +177 -0
  187. data/test/test_pape.rb +248 -0
  188. data/test/test_parsehtml.rb +79 -0
  189. data/test/test_responses.rb +63 -0
  190. data/test/test_server.rb +2455 -0
  191. data/test/test_sreg.rb +479 -0
  192. data/test/test_stores.rb +292 -0
  193. data/test/test_trustroot.rb +111 -0
  194. data/test/test_urinorm.rb +34 -0
  195. data/test/test_util.rb +145 -0
  196. data/test/test_xrds.rb +167 -0
  197. data/test/test_xri.rb +48 -0
  198. data/test/test_xrires.rb +67 -0
  199. data/test/test_yadis_discovery.rb +218 -0
  200. metadata +268 -0
@@ -0,0 +1,84 @@
1
+ require 'openid/store/interface'
2
+ module OpenID
3
+ module Store
4
+ # An in-memory implementation of Store. This class is mainly used
5
+ # for testing, though it may be useful for long-running single
6
+ # process apps. Note that this store is NOT thread-safe.
7
+ #
8
+ # You should probably be looking at OpenID::Store::Filesystem
9
+ class Memory < Interface
10
+
11
+ def initialize
12
+ @associations = {}
13
+ @associations.default = {}
14
+ @nonces = {}
15
+ end
16
+
17
+ def store_association(server_url, assoc)
18
+ assocs = @associations[server_url]
19
+ @associations[server_url] = assocs.merge({assoc.handle => deepcopy(assoc)})
20
+ end
21
+
22
+ def get_association(server_url, handle=nil)
23
+ assocs = @associations[server_url]
24
+ assoc = nil
25
+ if handle
26
+ assoc = assocs[handle]
27
+ else
28
+ assoc = assocs.values.sort{|a,b| a.issued <=> b.issued}[-1]
29
+ end
30
+
31
+ return assoc
32
+ end
33
+
34
+ def remove_association(server_url, handle)
35
+ assocs = @associations[server_url]
36
+ if assocs.delete(handle)
37
+ return true
38
+ else
39
+ return false
40
+ end
41
+ end
42
+
43
+ def use_nonce(server_url, timestamp, salt)
44
+ return false if (timestamp - Time.now.to_i).abs > Nonce.skew
45
+ nonce = [server_url, timestamp, salt].join('')
46
+ return false if @nonces[nonce]
47
+ @nonces[nonce] = timestamp
48
+ return true
49
+ end
50
+
51
+ def cleanup_associations
52
+ count = 0
53
+ @associations.each{|server_url, assocs|
54
+ assocs.each{|handle, assoc|
55
+ if assoc.expires_in == 0
56
+ assocs.delete(handle)
57
+ count += 1
58
+ end
59
+ }
60
+ }
61
+ return count
62
+ end
63
+
64
+ def cleanup_nonces
65
+ count = 0
66
+ now = Time.now.to_i
67
+ @nonces.each{|nonce, timestamp|
68
+ if (timestamp - now).abs > Nonce.skew
69
+ @nonces.delete(nonce)
70
+ count += 1
71
+ end
72
+ }
73
+ return count
74
+ end
75
+
76
+ protected
77
+
78
+ def deepcopy(o)
79
+ Marshal.load(Marshal.dump(o))
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,68 @@
1
+ require 'openid/cryptutil'
2
+ require 'date'
3
+ require 'time'
4
+
5
+ module OpenID
6
+ module Nonce
7
+ DEFAULT_SKEW = 60*60*5
8
+ TIME_FMT = '%Y-%m-%dT%H:%M:%SZ'
9
+ TIME_STR_LEN = '0000-00-00T00:00:00Z'.size
10
+ @@NONCE_CHRS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
11
+ TIME_VALIDATOR = /\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ/
12
+
13
+ @skew = DEFAULT_SKEW
14
+
15
+ # The allowed nonce time skew in seconds. Defaults to 5 hours.
16
+ # Used for checking nonce validity, and by stores' cleanup methods.
17
+ def Nonce.skew
18
+ @skew
19
+ end
20
+
21
+ def Nonce.skew=(new_skew)
22
+ @skew = new_skew
23
+ end
24
+
25
+ # Extract timestamp from a nonce string
26
+ def Nonce.split_nonce(nonce_str)
27
+ timestamp_str = nonce_str[0...TIME_STR_LEN]
28
+ raise ArgumentError if timestamp_str.size < TIME_STR_LEN
29
+ raise ArgumentError unless timestamp_str.match(TIME_VALIDATOR)
30
+ ts = Time.parse(timestamp_str).to_i
31
+ raise ArgumentError if ts < 0
32
+ return ts, nonce_str[TIME_STR_LEN..-1]
33
+ end
34
+
35
+ # Is the timestamp that is part of the specified nonce string
36
+ # within the allowed clock-skew of the current time?
37
+ def Nonce.check_timestamp(nonce_str, allowed_skew=nil, now=nil)
38
+ allowed_skew = skew if allowed_skew.nil?
39
+ begin
40
+ stamp, foo = split_nonce(nonce_str)
41
+ rescue ArgumentError # bad timestamp
42
+ return false
43
+ end
44
+ now = Time.now.to_i unless now
45
+
46
+ # times before this are too old
47
+ past = now - allowed_skew
48
+
49
+ # times newer than this are too far in the future
50
+ future = now + allowed_skew
51
+
52
+ return (past <= stamp and stamp <= future)
53
+ end
54
+
55
+ # generate a nonce with the specified timestamp (defaults to now)
56
+ def Nonce.mk_nonce(time = nil)
57
+ salt = CryptUtil::random_string(6, @@NONCE_CHRS)
58
+ if time.nil?
59
+ t = Time.now.getutc
60
+ else
61
+ t = Time.at(time).getutc
62
+ end
63
+ time_str = t.strftime(TIME_FMT)
64
+ return time_str + salt
65
+ end
66
+
67
+ end
68
+ end
@@ -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
+