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 @@
1
+ # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/about'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/breakpointer'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/console'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/destroy'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/generate'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/performance/benchmarker'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/performance/profiler'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/plugin'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/process/reaper'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/process/spawner'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/process/spinner'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/runner'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/server'
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require 'login_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class LoginController; def rescue_action(e) raise e end; end
6
+
7
+ class LoginControllerTest < Test::Unit::TestCase
8
+ def setup
9
+ @controller = LoginController.new
10
+ @request = ActionController::TestRequest.new
11
+ @response = ActionController::TestResponse.new
12
+ end
13
+
14
+ # Replace this with your real tests.
15
+ def test_truth
16
+ assert true
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require 'server_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class ServerController; def rescue_action(e) raise e end; end
6
+
7
+ class ServerControllerTest < Test::Unit::TestCase
8
+ def setup
9
+ @controller = ServerController.new
10
+ @request = ActionController::TestRequest.new
11
+ @response = ActionController::TestResponse.new
12
+ end
13
+
14
+ # Replace this with your real tests.
15
+ def test_truth
16
+ assert true
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
3
+ require 'test_help'
4
+
5
+ class Test::Unit::TestCase
6
+ # Transactional fixtures accelerate your tests by wrapping each test method
7
+ # in a transaction that's rolled back on completion. This ensures that the
8
+ # test database remains unchanged so your fixtures don't have to be reloaded
9
+ # between every test method. Fewer database queries means faster tests.
10
+ #
11
+ # Read Mike Clark's excellent walkthrough at
12
+ # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
13
+ #
14
+ # Every Active Record database supports transactions except MyISAM tables
15
+ # in MySQL. Turn off transactional fixtures in this case; however, if you
16
+ # don't care one way or the other, switching from MyISAM to InnoDB tables
17
+ # is recommended.
18
+ self.use_transactional_fixtures = true
19
+
20
+ # Instantiated fixtures are slow, but give you @david where otherwise you
21
+ # would need people(:david). If you don't want to migrate your existing
22
+ # test cases which use the @david style and don't mind the speed hit (each
23
+ # instantiated fixtures translates to a database query per test method),
24
+ # then set this back to true.
25
+ self.use_instantiated_fixtures = false
26
+
27
+ # Add more helper methods to be used by all tests here...
28
+ end
@@ -0,0 +1,112 @@
1
+ # Copyright (C) 2001 Daiki Ueno <ueno@unixuser.org>
2
+ # This library is distributed under the terms of the Ruby license.
3
+
4
+ # This module provides common interface to HMAC engines.
5
+ # HMAC standard is documented in RFC 2104:
6
+ #
7
+ # H. Krawczyk et al., "HMAC: Keyed-Hashing for Message Authentication",
8
+ # RFC 2104, February 1997
9
+ #
10
+ # These APIs are inspired by JCE 1.2's javax.crypto.Mac interface.
11
+ #
12
+ # <URL:http://java.sun.com/security/JCE1.2/spec/apidoc/javax/crypto/Mac.html>
13
+
14
+ module HMAC
15
+ class Base
16
+ def initialize(algorithm, block_size, output_length, key)
17
+ @algorithm = algorithm
18
+ @block_size = block_size
19
+ @output_length = output_length
20
+ @status = STATUS_UNDEFINED
21
+ @key_xor_ipad = ''
22
+ @key_xor_opad = ''
23
+ set_key(key) unless key.nil?
24
+ end
25
+
26
+ private
27
+ def check_status
28
+ unless @status == STATUS_INITIALIZED
29
+ raise RuntimeError,
30
+ "The underlying hash algorithm has not yet been initialized."
31
+ end
32
+ end
33
+
34
+ public
35
+ def set_key(key)
36
+ # If key is longer than the block size, apply hash function
37
+ # to key and use the result as a real key.
38
+ key = @algorithm.digest(key) if key.size > @block_size
39
+ key_xor_ipad = "\x36" * @block_size
40
+ key_xor_opad = "\x5C" * @block_size
41
+ for i in 0 .. key.size - 1
42
+ key_xor_ipad[i] ^= key[i]
43
+ key_xor_opad[i] ^= key[i]
44
+ end
45
+ @key_xor_ipad = key_xor_ipad
46
+ @key_xor_opad = key_xor_opad
47
+ @md = @algorithm.new
48
+ @status = STATUS_INITIALIZED
49
+ end
50
+
51
+ def reset_key
52
+ @key_xor_ipad.gsub!(/./, '?')
53
+ @key_xor_opad.gsub!(/./, '?')
54
+ @key_xor_ipad[0..-1] = ''
55
+ @key_xor_opad[0..-1] = ''
56
+ @status = STATUS_UNDEFINED
57
+ end
58
+
59
+ def update(text)
60
+ check_status
61
+ # perform inner H
62
+ md = @algorithm.new
63
+ md.update(@key_xor_ipad)
64
+ md.update(text)
65
+ str = md.digest
66
+ # perform outer H
67
+ md = @algorithm.new
68
+ md.update(@key_xor_opad)
69
+ md.update(str)
70
+ @md = md
71
+ end
72
+ alias << update
73
+
74
+ def digest
75
+ check_status
76
+ @md.digest
77
+ end
78
+
79
+ def hexdigest
80
+ check_status
81
+ @md.hexdigest
82
+ end
83
+ alias to_s hexdigest
84
+
85
+ # These two class methods below are safer than using above
86
+ # instance methods combinatorially because an instance will have
87
+ # held a key even if it's no longer in use.
88
+ def Base.digest(key, text)
89
+ begin
90
+ hmac = self.new(key)
91
+ hmac.update(text)
92
+ hmac.digest
93
+ ensure
94
+ hmac.reset_key
95
+ end
96
+ end
97
+
98
+ def Base.hexdigest(key, text)
99
+ begin
100
+ hmac = self.new(key)
101
+ hmac.update(text)
102
+ hmac.hexdigest
103
+ ensure
104
+ hmac.reset_key
105
+ end
106
+ end
107
+
108
+ private_class_method :new, :digest, :hexdigest
109
+ end
110
+
111
+ STATUS_UNDEFINED, STATUS_INITIALIZED = 0, 1
112
+ end
@@ -0,0 +1,11 @@
1
+ require 'hmac/hmac'
2
+ require 'digest/sha1'
3
+
4
+ module HMAC
5
+ class SHA1 < Base
6
+ def initialize(key = nil)
7
+ super(Digest::SHA1, 64, 20, key)
8
+ end
9
+ public_class_method :new, :digest, :hexdigest
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ require 'hmac/hmac'
2
+ require 'digest/sha2'
3
+
4
+ module HMAC
5
+ class SHA256 < Base
6
+ def initialize(key = nil)
7
+ super(Digest::SHA256, 64, 32, key)
8
+ end
9
+ public_class_method :new, :digest, :hexdigest
10
+ end
11
+
12
+ class SHA384 < Base
13
+ def initialize(key = nil)
14
+ super(Digest::SHA384, 128, 48, key)
15
+ end
16
+ public_class_method :new, :digest, :hexdigest
17
+ end
18
+
19
+ class SHA512 < Base
20
+ def initialize(key = nil)
21
+ super(Digest::SHA512, 128, 64, key)
22
+ end
23
+ public_class_method :new, :digest, :hexdigest
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright 2006-2007 JanRain, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you
4
+ # may not use this file except in compliance with the License. You may
5
+ # obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12
+ # implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+
15
+ require "openid/version"
16
+ require 'openid/store'
17
+ require 'openid/yadis'
18
+ require "openid/consumer"
19
+ require 'openid/server'
20
+
21
+ module OpenID
22
+ end
@@ -0,0 +1,249 @@
1
+ require "openid/kvform"
2
+ require "openid/util"
3
+ require "openid/cryptutil"
4
+ require "openid/message"
5
+
6
+ module OpenID
7
+
8
+ def self.get_secret_size(assoc_type)
9
+ if assoc_type == 'HMAC-SHA1'
10
+ return 20
11
+ elsif assoc_type == 'HMAC-SHA256'
12
+ return 32
13
+ else
14
+ raise ArgumentError("Unsupported association type: #{assoc_type}")
15
+ end
16
+ end
17
+
18
+ # An Association holds the shared secret between a relying party and
19
+ # an OpenID provider.
20
+ class Association
21
+ attr_reader :handle, :secret, :issued, :lifetime, :assoc_type
22
+
23
+ FIELD_ORDER =
24
+ [:version, :handle, :secret, :issued, :lifetime, :assoc_type,]
25
+
26
+ # Load a serialized Association
27
+ def self.deserialize(serialized)
28
+ parsed = Util.kv_to_seq(serialized)
29
+ parsed_fields = parsed.map{|k, v| k.to_sym}
30
+ if parsed_fields != FIELD_ORDER
31
+ raise ProtocolError, 'Unexpected fields in serialized association'\
32
+ " (Expected #{FIELD_ORDER.inspect}, got #{parsed_fields.inspect})"
33
+ end
34
+ version, handle, secret64, issued_s, lifetime_s, assoc_type =
35
+ parsed.map {|field, value| value}
36
+ if version != '2'
37
+ raise ProtocolError, "Attempted to deserialize unsupported version "\
38
+ "(#{parsed[0][1].inspect})"
39
+ end
40
+
41
+ self.new(handle,
42
+ Util.from_base64(secret64),
43
+ Time.at(issued_s.to_i),
44
+ lifetime_s.to_i,
45
+ assoc_type)
46
+ end
47
+
48
+ # Create an Association with an issued time of now
49
+ def self.from_expires_in(expires_in, handle, secret, assoc_type)
50
+ issued = Time.now
51
+ self.new(handle, secret, issued, expires_in, assoc_type)
52
+ end
53
+
54
+ def initialize(handle, secret, issued, lifetime, assoc_type)
55
+ @handle = handle
56
+ @secret = secret
57
+ @issued = issued
58
+ @lifetime = lifetime
59
+ @assoc_type = assoc_type
60
+ end
61
+
62
+ # Serialize the association to a form that's consistent across
63
+ # JanRain OpenID libraries.
64
+ def serialize
65
+ data = {
66
+ :version => '2',
67
+ :handle => handle,
68
+ :secret => Util.to_base64(secret),
69
+ :issued => issued.to_i.to_s,
70
+ :lifetime => lifetime.to_i.to_s,
71
+ :assoc_type => assoc_type,
72
+ }
73
+
74
+ Util.assert(data.length == FIELD_ORDER.length)
75
+
76
+ pairs = FIELD_ORDER.map{|field| [field.to_s, data[field]]}
77
+ return Util.seq_to_kv(pairs, strict=true)
78
+ end
79
+
80
+ # The number of seconds until this association expires
81
+ def expires_in(now=nil)
82
+ if now.nil?
83
+ now = Time.now.to_i
84
+ else
85
+ now = now.to_i
86
+ end
87
+ time_diff = (issued.to_i + lifetime) - now
88
+ if time_diff < 0
89
+ return 0
90
+ else
91
+ return time_diff
92
+ end
93
+ end
94
+
95
+ # Generate a signature for a sequence of [key, value] pairs
96
+ def sign(pairs)
97
+ kv = Util.seq_to_kv(pairs)
98
+ case assoc_type
99
+ when 'HMAC-SHA1'
100
+ CryptUtil.hmac_sha1(@secret, kv)
101
+ when 'HMAC-SHA256'
102
+ CryptUtil.hmac_sha256(@secret, kv)
103
+ else
104
+ raise ProtocolError, "Association has unknown type: "\
105
+ "#{assoc_type.inspect}"
106
+ end
107
+ end
108
+
109
+ # Generate the list of pairs that form the signed elements of the
110
+ # given message
111
+ def make_pairs(message)
112
+ signed = message.get_arg(OPENID_NS, 'signed')
113
+ if signed.nil?
114
+ raise ProtocolError, 'Missing signed list'
115
+ end
116
+ signed_fields = signed.split(',', -1)
117
+ data = message.to_post_args
118
+ signed_fields.map {|field| [field, data.fetch('openid.'+field,'')] }
119
+ end
120
+
121
+ # Return whether the message's signature passes
122
+ def check_message_signature(message)
123
+ message_sig = message.get_arg(OPENID_NS, 'sig')
124
+ if message_sig.nil?
125
+ raise ProtocolError, "#{message} has no sig."
126
+ end
127
+ calculated_sig = get_message_signature(message)
128
+ return CryptUtil.const_eq(calculated_sig, message_sig)
129
+ end
130
+
131
+ # Get the signature for this message
132
+ def get_message_signature(message)
133
+ Util.to_base64(sign(make_pairs(message)))
134
+ end
135
+
136
+ def ==(other)
137
+ (other.class == self.class and
138
+ other.handle == self.handle and
139
+ other.secret == self.secret and
140
+
141
+ # The internals of the time objects seemed to differ
142
+ # in an opaque way when serializing/unserializing.
143
+ # I don't think this will be a problem.
144
+ other.issued.to_i == self.issued.to_i and
145
+
146
+ other.lifetime == self.lifetime and
147
+ other.assoc_type == self.assoc_type)
148
+ end
149
+
150
+ # Add a signature (and a signed list) to a message.
151
+ def sign_message(message)
152
+ if (message.has_key?(OPENID_NS, 'sig') or
153
+ message.has_key?(OPENID_NS, 'signed'))
154
+ raise ArgumentError, 'Message already has signed list or signature'
155
+ end
156
+
157
+ extant_handle = message.get_arg(OPENID_NS, 'assoc_handle')
158
+ if extant_handle and extant_handle != self.handle
159
+ raise ArgumentError, "Message has a different association handle"
160
+ end
161
+
162
+ signed_message = message.copy()
163
+ signed_message.set_arg(OPENID_NS, 'assoc_handle', self.handle)
164
+ message_keys = signed_message.to_post_args.keys()
165
+
166
+ signed_list = []
167
+ message_keys.each { |k|
168
+ if k.starts_with?('openid.')
169
+ signed_list << k[7..-1]
170
+ end
171
+ }
172
+
173
+ signed_list << 'signed'
174
+ signed_list.sort!
175
+
176
+ signed_message.set_arg(OPENID_NS, 'signed', signed_list.join(','))
177
+ sig = get_message_signature(signed_message)
178
+ signed_message.set_arg(OPENID_NS, 'sig', sig)
179
+ return signed_message
180
+ end
181
+ end
182
+
183
+ class AssociationNegotiator
184
+ attr_reader :allowed_types
185
+
186
+ def self.get_session_types(assoc_type)
187
+ case assoc_type
188
+ when 'HMAC-SHA1'
189
+ ['DH-SHA1', 'no-encryption']
190
+ when 'HMAC-SHA256'
191
+ ['DH-SHA256', 'no-encryption']
192
+ else
193
+ raise ProtocolError, "Unknown association type #{assoc_type.inspect}"
194
+ end
195
+ end
196
+
197
+ def self.check_session_type(assoc_type, session_type)
198
+ if !get_session_types(assoc_type).include?(session_type)
199
+ raise ProtocolError, "Session type #{session_type.inspect} not "\
200
+ "valid for association type #{assoc_type.inspect}"
201
+ end
202
+ end
203
+
204
+ def initialize(allowed_types)
205
+ self.allowed_types=(allowed_types)
206
+ end
207
+
208
+ def copy
209
+ Marshal.load(Marshal.dump(self))
210
+ end
211
+
212
+ def allowed_types=(allowed_types)
213
+ allowed_types.each do |assoc_type, session_type|
214
+ self.class.check_session_type(assoc_type, session_type)
215
+ end
216
+ @allowed_types = allowed_types
217
+ end
218
+
219
+ def add_allowed_type(assoc_type, session_type=nil)
220
+ if session_type.nil?
221
+ session_types = self.class.get_session_types(assoc_type)
222
+ else
223
+ self.class.check_session_type(assoc_type, session_type)
224
+ session_types = [session_type]
225
+ end
226
+ for session_type in session_types do
227
+ @allowed_types << [assoc_type, session_type]
228
+ end
229
+ end
230
+
231
+ def allowed?(assoc_type, session_type)
232
+ @allowed_types.include?([assoc_type, session_type])
233
+ end
234
+
235
+ def get_allowed_type
236
+ @allowed_types.empty? ? nil : @allowed_types[0]
237
+ end
238
+ end
239
+
240
+ DefaultNegotiator =
241
+ AssociationNegotiator.new([['HMAC-SHA1', 'DH-SHA1'],
242
+ ['HMAC-SHA1', 'no-encryption'],
243
+ ['HMAC-SHA256', 'DH-SHA256'],
244
+ ['HMAC-SHA256', 'no-encryption']])
245
+
246
+ EncryptedNegotiator =
247
+ AssociationNegotiator.new([['HMAC-SHA1', 'DH-SHA1'],
248
+ ['HMAC-SHA256', 'DH-SHA256']])
249
+ end