nov-ruby-openid 2.1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (203) hide show
  1. data/CHANGELOG +215 -0
  2. data/CHANGES-2.1.0 +36 -0
  3. data/INSTALL +47 -0
  4. data/LICENSE +210 -0
  5. data/NOTICE +2 -0
  6. data/README +81 -0
  7. data/Rakefile +98 -0
  8. data/UPGRADE +127 -0
  9. data/VERSION +1 -0
  10. data/contrib/google/ruby-openid-apps-discovery-1.0.gem +0 -0
  11. data/contrib/google/ruby-openid-apps-discovery-1.01.gem +0 -0
  12. data/examples/README +32 -0
  13. data/examples/active_record_openid_store/README +58 -0
  14. data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +24 -0
  15. data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
  16. data/examples/active_record_openid_store/init.rb +8 -0
  17. data/examples/active_record_openid_store/lib/association.rb +10 -0
  18. data/examples/active_record_openid_store/lib/nonce.rb +3 -0
  19. data/examples/active_record_openid_store/lib/open_id_setting.rb +4 -0
  20. data/examples/active_record_openid_store/lib/openid_ar_store.rb +57 -0
  21. data/examples/active_record_openid_store/test/store_test.rb +212 -0
  22. data/examples/discover +49 -0
  23. data/examples/rails_openid/README +153 -0
  24. data/examples/rails_openid/Rakefile +10 -0
  25. data/examples/rails_openid/app/controllers/application.rb +4 -0
  26. data/examples/rails_openid/app/controllers/consumer_controller.rb +122 -0
  27. data/examples/rails_openid/app/controllers/login_controller.rb +45 -0
  28. data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
  29. data/examples/rails_openid/app/helpers/application_helper.rb +3 -0
  30. data/examples/rails_openid/app/helpers/login_helper.rb +2 -0
  31. data/examples/rails_openid/app/helpers/server_helper.rb +9 -0
  32. data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
  33. data/examples/rails_openid/app/views/layouts/server.rhtml +68 -0
  34. data/examples/rails_openid/app/views/login/index.rhtml +56 -0
  35. data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
  36. data/examples/rails_openid/config/boot.rb +19 -0
  37. data/examples/rails_openid/config/database.yml +74 -0
  38. data/examples/rails_openid/config/environment.rb +54 -0
  39. data/examples/rails_openid/config/environments/development.rb +19 -0
  40. data/examples/rails_openid/config/environments/production.rb +19 -0
  41. data/examples/rails_openid/config/environments/test.rb +19 -0
  42. data/examples/rails_openid/config/routes.rb +24 -0
  43. data/examples/rails_openid/doc/README_FOR_APP +2 -0
  44. data/examples/rails_openid/public/.htaccess +40 -0
  45. data/examples/rails_openid/public/404.html +8 -0
  46. data/examples/rails_openid/public/500.html +8 -0
  47. data/examples/rails_openid/public/dispatch.cgi +12 -0
  48. data/examples/rails_openid/public/dispatch.fcgi +26 -0
  49. data/examples/rails_openid/public/dispatch.rb +12 -0
  50. data/examples/rails_openid/public/favicon.ico +0 -0
  51. data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
  52. data/examples/rails_openid/public/javascripts/controls.js +750 -0
  53. data/examples/rails_openid/public/javascripts/dragdrop.js +584 -0
  54. data/examples/rails_openid/public/javascripts/effects.js +854 -0
  55. data/examples/rails_openid/public/javascripts/prototype.js +1785 -0
  56. data/examples/rails_openid/public/robots.txt +1 -0
  57. data/examples/rails_openid/script/about +3 -0
  58. data/examples/rails_openid/script/breakpointer +3 -0
  59. data/examples/rails_openid/script/console +3 -0
  60. data/examples/rails_openid/script/destroy +3 -0
  61. data/examples/rails_openid/script/generate +3 -0
  62. data/examples/rails_openid/script/performance/benchmarker +3 -0
  63. data/examples/rails_openid/script/performance/profiler +3 -0
  64. data/examples/rails_openid/script/plugin +3 -0
  65. data/examples/rails_openid/script/process/reaper +3 -0
  66. data/examples/rails_openid/script/process/spawner +3 -0
  67. data/examples/rails_openid/script/process/spinner +3 -0
  68. data/examples/rails_openid/script/runner +3 -0
  69. data/examples/rails_openid/script/server +3 -0
  70. data/examples/rails_openid/test/functional/login_controller_test.rb +18 -0
  71. data/examples/rails_openid/test/functional/server_controller_test.rb +18 -0
  72. data/examples/rails_openid/test/test_helper.rb +28 -0
  73. data/lib/hmac/hmac.rb +112 -0
  74. data/lib/hmac/sha1.rb +11 -0
  75. data/lib/hmac/sha2.rb +25 -0
  76. data/lib/openid.rb +20 -0
  77. data/lib/openid/association.rb +249 -0
  78. data/lib/openid/consumer.rb +395 -0
  79. data/lib/openid/consumer/associationmanager.rb +344 -0
  80. data/lib/openid/consumer/checkid_request.rb +186 -0
  81. data/lib/openid/consumer/discovery.rb +497 -0
  82. data/lib/openid/consumer/discovery_manager.rb +123 -0
  83. data/lib/openid/consumer/html_parse.rb +134 -0
  84. data/lib/openid/consumer/idres.rb +523 -0
  85. data/lib/openid/consumer/responses.rb +148 -0
  86. data/lib/openid/cryptutil.rb +115 -0
  87. data/lib/openid/dh.rb +89 -0
  88. data/lib/openid/extension.rb +39 -0
  89. data/lib/openid/extensions/ax.rb +539 -0
  90. data/lib/openid/extensions/oauth.rb +91 -0
  91. data/lib/openid/extensions/pape.rb +179 -0
  92. data/lib/openid/extensions/sreg.rb +277 -0
  93. data/lib/openid/extensions/ui.rb +53 -0
  94. data/lib/openid/extras.rb +11 -0
  95. data/lib/openid/fetchers.rb +258 -0
  96. data/lib/openid/kvform.rb +136 -0
  97. data/lib/openid/kvpost.rb +58 -0
  98. data/lib/openid/message.rb +553 -0
  99. data/lib/openid/protocolerror.rb +8 -0
  100. data/lib/openid/server.rb +1544 -0
  101. data/lib/openid/store/filesystem.rb +271 -0
  102. data/lib/openid/store/interface.rb +75 -0
  103. data/lib/openid/store/memcache.rb +107 -0
  104. data/lib/openid/store/memory.rb +84 -0
  105. data/lib/openid/store/nonce.rb +68 -0
  106. data/lib/openid/trustroot.rb +349 -0
  107. data/lib/openid/urinorm.rb +75 -0
  108. data/lib/openid/util.rb +110 -0
  109. data/lib/openid/yadis/accept.rb +148 -0
  110. data/lib/openid/yadis/constants.rb +21 -0
  111. data/lib/openid/yadis/discovery.rb +153 -0
  112. data/lib/openid/yadis/filters.rb +205 -0
  113. data/lib/openid/yadis/htmltokenizer.rb +305 -0
  114. data/lib/openid/yadis/parsehtml.rb +45 -0
  115. data/lib/openid/yadis/services.rb +42 -0
  116. data/lib/openid/yadis/xrds.rb +155 -0
  117. data/lib/openid/yadis/xri.rb +90 -0
  118. data/lib/openid/yadis/xrires.rb +99 -0
  119. data/setup.rb +1551 -0
  120. data/test/data/accept.txt +124 -0
  121. data/test/data/dh.txt +29 -0
  122. data/test/data/example-xrds.xml +14 -0
  123. data/test/data/linkparse.txt +587 -0
  124. data/test/data/n2b64 +650 -0
  125. data/test/data/test1-discover.txt +137 -0
  126. data/test/data/test1-parsehtml.txt +152 -0
  127. data/test/data/test_discover/malformed_meta_tag.html +19 -0
  128. data/test/data/test_discover/openid.html +11 -0
  129. data/test/data/test_discover/openid2.html +11 -0
  130. data/test/data/test_discover/openid2_xrds.xml +12 -0
  131. data/test/data/test_discover/openid2_xrds_no_local_id.xml +11 -0
  132. data/test/data/test_discover/openid_1_and_2.html +11 -0
  133. data/test/data/test_discover/openid_1_and_2_xrds.xml +16 -0
  134. data/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
  135. data/test/data/test_discover/openid_and_yadis.html +12 -0
  136. data/test/data/test_discover/openid_no_delegate.html +10 -0
  137. data/test/data/test_discover/openid_utf8.html +11 -0
  138. data/test/data/test_discover/yadis_0entries.xml +12 -0
  139. data/test/data/test_discover/yadis_2_bad_local_id.xml +15 -0
  140. data/test/data/test_discover/yadis_2entries_delegate.xml +22 -0
  141. data/test/data/test_discover/yadis_2entries_idp.xml +21 -0
  142. data/test/data/test_discover/yadis_another_delegate.xml +14 -0
  143. data/test/data/test_discover/yadis_idp.xml +12 -0
  144. data/test/data/test_discover/yadis_idp_delegate.xml +13 -0
  145. data/test/data/test_discover/yadis_no_delegate.xml +11 -0
  146. data/test/data/test_xrds/=j3h.2007.11.14.xrds +25 -0
  147. data/test/data/test_xrds/README +12 -0
  148. data/test/data/test_xrds/delegated-20060809-r1.xrds +34 -0
  149. data/test/data/test_xrds/delegated-20060809-r2.xrds +34 -0
  150. data/test/data/test_xrds/delegated-20060809.xrds +34 -0
  151. data/test/data/test_xrds/no-xrd.xml +7 -0
  152. data/test/data/test_xrds/not-xrds.xml +2 -0
  153. data/test/data/test_xrds/prefixsometimes.xrds +34 -0
  154. data/test/data/test_xrds/ref.xrds +109 -0
  155. data/test/data/test_xrds/sometimesprefix.xrds +34 -0
  156. data/test/data/test_xrds/spoof1.xrds +25 -0
  157. data/test/data/test_xrds/spoof2.xrds +25 -0
  158. data/test/data/test_xrds/spoof3.xrds +37 -0
  159. data/test/data/test_xrds/status222.xrds +9 -0
  160. data/test/data/test_xrds/subsegments.xrds +58 -0
  161. data/test/data/test_xrds/valid-populated-xrds.xml +39 -0
  162. data/test/data/trustroot.txt +153 -0
  163. data/test/data/urinorm.txt +79 -0
  164. data/test/discoverdata.rb +131 -0
  165. data/test/test_accept.rb +170 -0
  166. data/test/test_association.rb +266 -0
  167. data/test/test_associationmanager.rb +917 -0
  168. data/test/test_ax.rb +690 -0
  169. data/test/test_checkid_request.rb +294 -0
  170. data/test/test_consumer.rb +257 -0
  171. data/test/test_cryptutil.rb +119 -0
  172. data/test/test_dh.rb +86 -0
  173. data/test/test_discover.rb +852 -0
  174. data/test/test_discovery_manager.rb +262 -0
  175. data/test/test_extension.rb +46 -0
  176. data/test/test_extras.rb +35 -0
  177. data/test/test_fetchers.rb +565 -0
  178. data/test/test_filters.rb +270 -0
  179. data/test/test_idres.rb +963 -0
  180. data/test/test_kvform.rb +165 -0
  181. data/test/test_kvpost.rb +65 -0
  182. data/test/test_linkparse.rb +101 -0
  183. data/test/test_message.rb +1116 -0
  184. data/test/test_nonce.rb +89 -0
  185. data/test/test_oauth.rb +175 -0
  186. data/test/test_openid_yadis.rb +178 -0
  187. data/test/test_pape.rb +247 -0
  188. data/test/test_parsehtml.rb +80 -0
  189. data/test/test_responses.rb +63 -0
  190. data/test/test_server.rb +2457 -0
  191. data/test/test_sreg.rb +479 -0
  192. data/test/test_stores.rb +298 -0
  193. data/test/test_trustroot.rb +113 -0
  194. data/test/test_ui.rb +93 -0
  195. data/test/test_urinorm.rb +35 -0
  196. data/test/test_util.rb +145 -0
  197. data/test/test_xrds.rb +169 -0
  198. data/test/test_xri.rb +48 -0
  199. data/test/test_xrires.rb +63 -0
  200. data/test/test_yadis_discovery.rb +220 -0
  201. data/test/testutil.rb +127 -0
  202. data/test/util.rb +53 -0
  203. metadata +336 -0
@@ -0,0 +1,148 @@
1
+ module OpenID
2
+ class Consumer
3
+ # Code returned when either the of the
4
+ # OpenID::OpenIDConsumer.begin_auth or OpenID::OpenIDConsumer.complete_auth
5
+ # methods return successfully.
6
+ SUCCESS = :success
7
+
8
+ # Code OpenID::OpenIDConsumer.complete_auth
9
+ # returns when the value it received indicated an invalid login.
10
+ FAILURE = :failure
11
+
12
+ # Code returned by OpenIDConsumer.complete_auth when the user
13
+ # cancels the operation from the server.
14
+ CANCEL = :cancel
15
+
16
+ # Code returned by OpenID::OpenIDConsumer.complete_auth when the
17
+ # OpenIDConsumer instance is in immediate mode and ther server sends back a
18
+ # URL for the user to login with.
19
+ SETUP_NEEDED = :setup_needed
20
+
21
+
22
+ module Response
23
+ attr_reader :endpoint
24
+
25
+ def status
26
+ self.class::STATUS
27
+ end
28
+
29
+ # The identity URL that has been authenticated; the Claimed Identifier.
30
+ # See also display_identifier.
31
+ def identity_url
32
+ @endpoint ? @endpoint.claimed_id : nil
33
+ end
34
+
35
+ # The display identifier is related to the Claimed Identifier, but the
36
+ # two are not always identical. The display identifier is something the
37
+ # user should recognize as what they entered, whereas the response's
38
+ # claimed identifier (in the identity_url attribute) may have extra
39
+ # information for better persistence.
40
+ #
41
+ # URLs will be stripped of their fragments for display. XRIs will
42
+ # display the human-readable identifier (i-name) instead of the
43
+ # persistent identifier (i-number).
44
+ #
45
+ # Use the display identifier in your user interface. Use identity_url
46
+ # for querying your database or authorization server, or other
47
+ # identifier equality comparisons.
48
+ def display_identifier
49
+ @endpoint ? @endpoint.display_identifier : nil
50
+ end
51
+ end
52
+
53
+ # A successful acknowledgement from the OpenID server that the
54
+ # supplied URL is, indeed controlled by the requesting agent.
55
+ class SuccessResponse
56
+ include Response
57
+
58
+ STATUS = SUCCESS
59
+
60
+ attr_reader :message, :signed_fields
61
+
62
+ def initialize(endpoint, message, signed_fields)
63
+ # Don't use :endpoint=, because endpoint should never be nil
64
+ # for a successfull transaction.
65
+ @endpoint = endpoint
66
+ @identity_url = endpoint.claimed_id
67
+ @message = message
68
+ @signed_fields = signed_fields
69
+ end
70
+
71
+ # Was this authentication response an OpenID 1 authentication
72
+ # response?
73
+ def is_openid1
74
+ @message.is_openid1
75
+ end
76
+
77
+ # Return whether a particular key is signed, regardless of its
78
+ # namespace alias
79
+ def signed?(ns_uri, ns_key)
80
+ @signed_fields.member?(@message.get_key(ns_uri, ns_key))
81
+ end
82
+
83
+ # Return the specified signed field if available, otherwise
84
+ # return default
85
+ def get_signed(ns_uri, ns_key, default=nil)
86
+ if signed?(ns_uri, ns_key)
87
+ return @message.get_arg(ns_uri, ns_key, default)
88
+ else
89
+ return default
90
+ end
91
+ end
92
+
93
+ # Get signed arguments from the response message. Return a dict
94
+ # of all arguments in the specified namespace. If any of the
95
+ # arguments are not signed, return nil.
96
+ def get_signed_ns(ns_uri)
97
+ msg_args = @message.get_args(ns_uri)
98
+ msg_args.each_key do |key|
99
+ if !signed?(ns_uri, key)
100
+ return nil
101
+ end
102
+ end
103
+ return msg_args
104
+ end
105
+
106
+ # Return response arguments in the specified namespace.
107
+ # If require_signed is true and the arguments are not signed,
108
+ # return nil.
109
+ def extension_response(namespace_uri, require_signed)
110
+ if require_signed
111
+ get_signed_ns(namespace_uri)
112
+ else
113
+ @message.get_args(namespace_uri)
114
+ end
115
+ end
116
+ end
117
+
118
+ class FailureResponse
119
+ include Response
120
+ STATUS = FAILURE
121
+
122
+ attr_reader :message, :contact, :reference
123
+ def initialize(endpoint, message, contact=nil, reference=nil)
124
+ @endpoint = endpoint
125
+ @message = message
126
+ @contact = contact
127
+ @reference = reference
128
+ end
129
+ end
130
+
131
+ class CancelResponse
132
+ include Response
133
+ STATUS = CANCEL
134
+ def initialize(endpoint)
135
+ @endpoint = endpoint
136
+ end
137
+ end
138
+
139
+ class SetupNeededResponse
140
+ include Response
141
+ STATUS = SETUP_NEEDED
142
+ def initialize(endpoint, setup_url)
143
+ @endpoint = endpoint
144
+ @setup_url = setup_url
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,115 @@
1
+ require "openid/util"
2
+ require "digest/sha1"
3
+ require "digest/sha2"
4
+ begin
5
+ require "digest/hmac"
6
+ rescue LoadError
7
+ begin
8
+ # Try loading the ruby-hmac files if they exist
9
+ require "hmac-sha1"
10
+ require "hmac-sha2"
11
+ rescue LoadError
12
+ # Nothing exists use included hmac files
13
+ require "hmac/sha1"
14
+ require "hmac/sha2"
15
+ end
16
+ end
17
+
18
+ module OpenID
19
+ # This module contains everything needed to perform low-level
20
+ # cryptograph and data manipulation tasks.
21
+ module CryptUtil
22
+
23
+ # Generate a random number, doing a little extra work to make it
24
+ # more likely that it's suitable for cryptography. If your system
25
+ # doesn't have /dev/urandom then this number is not
26
+ # cryptographically safe. See
27
+ # <http://www.cosine.org/2007/08/07/security-ruby-kernel-rand/>
28
+ # for more information. max is the largest possible value of such
29
+ # a random number, where the result will be less than max.
30
+ def CryptUtil.rand(max)
31
+ Kernel.srand()
32
+ return Kernel.rand(max)
33
+ end
34
+
35
+ def CryptUtil.sha1(text)
36
+ return Digest::SHA1.digest(text)
37
+ end
38
+
39
+ def CryptUtil.hmac_sha1(key, text)
40
+ if Digest.const_defined? :HMAC
41
+ Digest::HMAC.new(key,Digest::SHA1).update(text).digest
42
+ else
43
+ return HMAC::SHA1.digest(key, text)
44
+ end
45
+ end
46
+
47
+ def CryptUtil.sha256(text)
48
+ return Digest::SHA256.digest(text)
49
+ end
50
+
51
+ def CryptUtil.hmac_sha256(key, text)
52
+ if Digest.const_defined? :HMAC
53
+ Digest::HMAC.new(key,Digest::SHA256).update(text).digest
54
+ else
55
+ return HMAC::SHA256.digest(key, text)
56
+ end
57
+ end
58
+
59
+ # Generate a random string of the given length, composed of the
60
+ # specified characters. If chars is nil, generate a string
61
+ # composed of characters in the range 0..255.
62
+ def CryptUtil.random_string(length, chars=nil)
63
+ s = ""
64
+
65
+ unless chars.nil?
66
+ length.times { s << chars[rand(chars.length)] }
67
+ else
68
+ length.times { s << rand(256).chr }
69
+ end
70
+ return s
71
+ end
72
+
73
+ # Convert a number to its binary representation; return a string
74
+ # of bytes.
75
+ def CryptUtil.num_to_binary(n)
76
+ bits = n.to_s(2)
77
+ prepend = (8 - bits.length % 8)
78
+ bits = ('0' * prepend) + bits
79
+ return [bits].pack('B*')
80
+ end
81
+
82
+ # Convert a string of bytes into a number.
83
+ def CryptUtil.binary_to_num(s)
84
+ # taken from openid-ruby 0.0.1
85
+ s = "\000" * (4 - (s.length % 4)) + s
86
+ num = 0
87
+ s.unpack('N*').each do |x|
88
+ num <<= 32
89
+ num |= x
90
+ end
91
+ return num
92
+ end
93
+
94
+ # Encode a number as a base64-encoded byte string.
95
+ def CryptUtil.num_to_base64(l)
96
+ return OpenID::Util.to_base64(num_to_binary(l))
97
+ end
98
+
99
+ # Decode a base64 byte string to a number.
100
+ def CryptUtil.base64_to_num(s)
101
+ return binary_to_num(OpenID::Util.from_base64(s))
102
+ end
103
+
104
+ def CryptUtil.const_eq(s1, s2)
105
+ if s1.length != s2.length
106
+ return false
107
+ end
108
+ result = true
109
+ s1.length.times do |i|
110
+ result &= (s1[i] == s2[i])
111
+ end
112
+ return result
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,89 @@
1
+ require "openid/util"
2
+ require "openid/cryptutil"
3
+
4
+ module OpenID
5
+
6
+ # Encapsulates a Diffie-Hellman key exchange. This class is used
7
+ # internally by both the consumer and server objects.
8
+ #
9
+ # Read more about Diffie-Hellman on wikipedia:
10
+ # http://en.wikipedia.org/wiki/Diffie-Hellman
11
+
12
+ class DiffieHellman
13
+
14
+ # From the OpenID specification
15
+ @@default_mod = 155172898181473697471232257763715539915724801966915404479707795314057629378541917580651227423698188993727816152646631438561595825688188889951272158842675419950341258706556549803580104870537681476726513255747040765857479291291572334510643245094715007229621094194349783925984760375594985848253359305585439638443
16
+ @@default_gen = 2
17
+
18
+ attr_reader :modulus, :generator, :public
19
+
20
+ # A new DiffieHellman object, using the modulus and generator from
21
+ # the OpenID specification
22
+ def DiffieHellman.from_defaults
23
+ DiffieHellman.new(@@default_mod, @@default_gen)
24
+ end
25
+
26
+ def initialize(modulus=nil, generator=nil, priv=nil)
27
+ @modulus = modulus.nil? ? @@default_mod : modulus
28
+ @generator = generator.nil? ? @@default_gen : generator
29
+ set_private(priv.nil? ? OpenID::CryptUtil.rand(@modulus-2) + 1 : priv)
30
+ end
31
+
32
+ def get_shared_secret(composite)
33
+ DiffieHellman.powermod(composite, @private, @modulus)
34
+ end
35
+
36
+ def xor_secret(algorithm, composite, secret)
37
+ dh_shared = get_shared_secret(composite)
38
+ packed_dh_shared = OpenID::CryptUtil.num_to_binary(dh_shared)
39
+ hashed_dh_shared = algorithm.call(packed_dh_shared)
40
+ return DiffieHellman.strxor(secret, hashed_dh_shared)
41
+ end
42
+
43
+ def using_default_values?
44
+ @generator == @@default_gen && @modulus == @@default_mod
45
+ end
46
+
47
+ private
48
+ def set_private(priv)
49
+ @private = priv
50
+ @public = DiffieHellman.powermod(@generator, @private, @modulus)
51
+ end
52
+
53
+ def DiffieHellman.strxor(s, t)
54
+ if s.length != t.length
55
+ raise ArgumentError, "strxor: lengths don't match. " +
56
+ "Inputs were #{s.inspect} and #{t.inspect}"
57
+ end
58
+
59
+ if String.method_defined? :bytes
60
+ s.bytes.zip(t.bytes).map{|sb,tb| sb^tb}.pack('C*')
61
+ else
62
+ indices = 0...(s.length)
63
+ chrs = indices.collect {|i| (s[i]^t[i]).chr}
64
+ chrs.join("")
65
+ end
66
+ end
67
+
68
+ # This code is taken from this post:
69
+ # <http://blade.nagaokaut.ac.jp/cgi-bin/scat.\rb/ruby/ruby-talk/19098>
70
+ # by Eric Lee Green.
71
+ def DiffieHellman.powermod(x, n, q)
72
+ counter=0
73
+ n_p=n
74
+ y_p=1
75
+ z_p=x
76
+ while n_p != 0
77
+ if n_p[0]==1
78
+ y_p=(y_p*z_p) % q
79
+ end
80
+ n_p = n_p >> 1
81
+ z_p = (z_p * z_p) % q
82
+ counter += 1
83
+ end
84
+ return y_p
85
+ end
86
+
87
+ end
88
+
89
+ end
@@ -0,0 +1,39 @@
1
+ require 'openid/message'
2
+
3
+ module OpenID
4
+ # An interface for OpenID extensions.
5
+ class Extension < Object
6
+
7
+ def initialize
8
+ @ns_uri = nil
9
+ @ns_alias = nil
10
+ end
11
+
12
+ # Get the string arguments that should be added to an OpenID
13
+ # message for this extension.
14
+ def get_extension_args
15
+ raise NotImplementedError
16
+ end
17
+
18
+ # Add the arguments from this extension to the provided
19
+ # message, or create a new message containing only those
20
+ # arguments. Returns the message with added extension args.
21
+ def to_message(message = nil)
22
+ if message.nil?
23
+ # warnings.warn('Passing None to Extension.toMessage is deprecated. '
24
+ # 'Creating a message assuming you want OpenID 2.',
25
+ # DeprecationWarning, stacklevel=2)
26
+ Message.new(OPENID2_NS)
27
+ end
28
+ message = Message.new if message.nil?
29
+
30
+ implicit = message.is_openid1()
31
+
32
+ message.namespaces.add_alias(@ns_uri, @ns_alias, implicit)
33
+ # XXX python ignores keyerror if m.ns.getAlias(uri) == alias
34
+
35
+ message.update_args(@ns_uri, get_extension_args)
36
+ return message
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,539 @@
1
+ # Implements the OpenID attribute exchange specification, version 1.0
2
+
3
+ require 'openid/extension'
4
+ require 'openid/trustroot'
5
+ require 'openid/message'
6
+
7
+ module OpenID
8
+ module AX
9
+
10
+ UNLIMITED_VALUES = "unlimited"
11
+ MINIMUM_SUPPORTED_ALIAS_LENGTH = 32
12
+
13
+ # check alias for invalid characters, raise AXError if found
14
+ def self.check_alias(name)
15
+ if name.match(/(,|\.)/)
16
+ raise Error, ("Alias #{name.inspect} must not contain a "\
17
+ "comma or period.")
18
+ end
19
+ end
20
+
21
+ # Raised when data does not comply with AX 1.0 specification
22
+ class Error < ArgumentError
23
+ end
24
+
25
+ # Abstract class containing common code for attribute exchange messages
26
+ class AXMessage < Extension
27
+ attr_accessor :ns_alias, :mode, :ns_uri
28
+
29
+ NS_URI = 'http://openid.net/srv/ax/1.0'
30
+ def initialize
31
+ @ns_alias = 'ax'
32
+ @ns_uri = NS_URI
33
+ @mode = nil
34
+ end
35
+
36
+ protected
37
+
38
+ # Raise an exception if the mode in the attribute exchange
39
+ # arguments does not match what is expected for this class.
40
+ def check_mode(ax_args)
41
+ actual_mode = ax_args['mode']
42
+ if actual_mode != @mode
43
+ raise Error, "Expected mode #{mode.inspect}, got #{actual_mode.inspect}"
44
+ end
45
+ end
46
+
47
+ def new_args
48
+ {'mode' => @mode}
49
+ end
50
+ end
51
+
52
+ # Represents a single attribute in an attribute exchange
53
+ # request. This should be added to an Request object in order to
54
+ # request the attribute.
55
+ #
56
+ # @ivar required: Whether the attribute will be marked as required
57
+ # when presented to the subject of the attribute exchange
58
+ # request.
59
+ # @type required: bool
60
+ #
61
+ # @ivar count: How many values of this type to request from the
62
+ # subject. Defaults to one.
63
+ # @type count: int
64
+ #
65
+ # @ivar type_uri: The identifier that determines what the attribute
66
+ # represents and how it is serialized. For example, one type URI
67
+ # representing dates could represent a Unix timestamp in base 10
68
+ # and another could represent a human-readable string.
69
+ # @type type_uri: str
70
+ #
71
+ # @ivar ns_alias: The name that should be given to this alias in the
72
+ # request. If it is not supplied, a generic name will be
73
+ # assigned. For example, if you want to call a Unix timestamp
74
+ # value 'tstamp', set its alias to that value. If two attributes
75
+ # in the same message request to use the same alias, the request
76
+ # will fail to be generated.
77
+ # @type alias: str or NoneType
78
+ class AttrInfo < Object
79
+ attr_reader :type_uri, :count, :ns_alias
80
+ attr_accessor :required
81
+ def initialize(type_uri, ns_alias=nil, required=false, count=1)
82
+ @type_uri = type_uri
83
+ @count = count
84
+ @required = required
85
+ @ns_alias = ns_alias
86
+ end
87
+
88
+ def wants_unlimited_values?
89
+ @count == UNLIMITED_VALUES
90
+ end
91
+ end
92
+
93
+ # Given a namespace mapping and a string containing a
94
+ # comma-separated list of namespace aliases, return a list of type
95
+ # URIs that correspond to those aliases.
96
+ # namespace_map: OpenID::NamespaceMap
97
+ def self.to_type_uris(namespace_map, alias_list_s)
98
+ return [] if alias_list_s.nil?
99
+ alias_list_s.split(',').inject([]) {|uris, name|
100
+ type_uri = namespace_map.get_namespace_uri(name)
101
+ raise IndexError, "No type defined for attribute name #{name.inspect}" if type_uri.nil?
102
+ uris << type_uri
103
+ }
104
+ end
105
+
106
+
107
+ # An attribute exchange 'fetch_request' message. This message is
108
+ # sent by a relying party when it wishes to obtain attributes about
109
+ # the subject of an OpenID authentication request.
110
+ class FetchRequest < AXMessage
111
+ attr_reader :requested_attributes
112
+ attr_accessor :update_url
113
+
114
+ MODE = 'fetch_request'
115
+
116
+ def initialize(update_url = nil)
117
+ super()
118
+ @mode = MODE
119
+ @requested_attributes = {}
120
+ @update_url = update_url
121
+ end
122
+
123
+ # Add an attribute to this attribute exchange request.
124
+ # attribute: AttrInfo, the attribute being requested
125
+ # Raises IndexError if the requested attribute is already present
126
+ # in this request.
127
+ def add(attribute)
128
+ if @requested_attributes[attribute.type_uri]
129
+ raise IndexError, "The attribute #{attribute.type_uri} has already been requested"
130
+ end
131
+ @requested_attributes[attribute.type_uri] = attribute
132
+ end
133
+
134
+ # Get the serialized form of this attribute fetch request.
135
+ # returns a hash of the arguments
136
+ def get_extension_args
137
+ aliases = NamespaceMap.new
138
+ required = []
139
+ if_available = []
140
+ ax_args = new_args
141
+ @requested_attributes.each{|type_uri, attribute|
142
+ if attribute.ns_alias
143
+ name = aliases.add_alias(type_uri, attribute.ns_alias)
144
+ else
145
+ name = aliases.add(type_uri)
146
+ end
147
+ if attribute.required
148
+ required << name
149
+ else
150
+ if_available << name
151
+ end
152
+ if attribute.count != 1
153
+ ax_args["count.#{name}"] = attribute.count.to_s
154
+ end
155
+ ax_args["type.#{name}"] = type_uri
156
+ }
157
+
158
+ unless required.empty?
159
+ ax_args['required'] = required.join(',')
160
+ end
161
+ unless if_available.empty?
162
+ ax_args['if_available'] = if_available.join(',')
163
+ end
164
+ return ax_args
165
+ end
166
+
167
+ # Get the type URIs for all attributes that have been marked
168
+ # as required.
169
+ def get_required_attrs
170
+ @requested_attributes.inject([]) {|required, (type_uri, attribute)|
171
+ if attribute.required
172
+ required << type_uri
173
+ else
174
+ required
175
+ end
176
+ }
177
+ end
178
+
179
+ # Extract a FetchRequest from an OpenID message
180
+ # message: OpenID::Message
181
+ # return a FetchRequest or nil if AX arguments are not present
182
+ def self.from_openid_request(oidreq)
183
+ message = oidreq.message
184
+ ax_args = message.get_args(NS_URI)
185
+ return nil if ax_args == {} or ax_args['mode'] != MODE
186
+ req = new
187
+ req.parse_extension_args(ax_args)
188
+
189
+ if req.update_url
190
+ realm = message.get_arg(OPENID_NS, 'realm',
191
+ message.get_arg(OPENID_NS, 'return_to'))
192
+ if realm.nil? or realm.empty?
193
+ raise Error, "Cannot validate update_url #{req.update_url.inspect} against absent realm"
194
+ end
195
+ tr = TrustRoot::TrustRoot.parse(realm)
196
+ unless tr.validate_url(req.update_url)
197
+ raise Error, "Update URL #{req.update_url.inspect} failed validation against realm #{realm.inspect}"
198
+ end
199
+ end
200
+
201
+ return req
202
+ end
203
+
204
+ def parse_extension_args(ax_args)
205
+ check_mode(ax_args)
206
+
207
+ aliases = NamespaceMap.new
208
+
209
+ ax_args.each{|k,v|
210
+ if k.index('type.') == 0
211
+ name = k[5..-1]
212
+ type_uri = v
213
+ aliases.add_alias(type_uri, name)
214
+
215
+ count_key = 'count.'+name
216
+ count_s = ax_args[count_key]
217
+ count = 1
218
+ if count_s
219
+ if count_s == UNLIMITED_VALUES
220
+ count = count_s
221
+ else
222
+ count = count_s.to_i
223
+ if count <= 0
224
+ raise Error, "Invalid value for count #{count_key.inspect}: #{count_s.inspect}"
225
+ end
226
+ end
227
+ end
228
+ add(AttrInfo.new(type_uri, name, false, count))
229
+ end
230
+ }
231
+
232
+ required = AX.to_type_uris(aliases, ax_args['required'])
233
+ required.each{|type_uri|
234
+ @requested_attributes[type_uri].required = true
235
+ }
236
+ if_available = AX.to_type_uris(aliases, ax_args['if_available'])
237
+ all_type_uris = required + if_available
238
+
239
+ aliases.namespace_uris.each{|type_uri|
240
+ unless all_type_uris.member? type_uri
241
+ raise Error, "Type URI #{type_uri.inspect} was in the request but not present in 'required' or 'if_available'"
242
+ end
243
+ }
244
+ @update_url = ax_args['update_url']
245
+ end
246
+
247
+ # return the list of AttrInfo objects contained in the FetchRequest
248
+ def attributes
249
+ @requested_attributes.values
250
+ end
251
+
252
+ # return the list of requested attribute type URIs
253
+ def requested_types
254
+ @requested_attributes.keys
255
+ end
256
+
257
+ def member?(type_uri)
258
+ ! @requested_attributes[type_uri].nil?
259
+ end
260
+
261
+ end
262
+
263
+ # Abstract class that implements a message that has attribute
264
+ # keys and values. It contains the common code between
265
+ # fetch_response and store_request.
266
+ class KeyValueMessage < AXMessage
267
+ attr_reader :data
268
+ def initialize
269
+ super()
270
+ @mode = nil
271
+ @data = {}
272
+ @data.default = []
273
+ end
274
+
275
+ # Add a single value for the given attribute type to the
276
+ # message. If there are already values specified for this type,
277
+ # this value will be sent in addition to the values already
278
+ # specified.
279
+ def add_value(type_uri, value)
280
+ @data[type_uri] = @data[type_uri] << value
281
+ end
282
+
283
+ # Set the values for the given attribute type. This replaces
284
+ # any values that have already been set for this attribute.
285
+ def set_values(type_uri, values)
286
+ @data[type_uri] = values
287
+ end
288
+
289
+ # Get the extension arguments for the key/value pairs
290
+ # contained in this message.
291
+ def _get_extension_kv_args(aliases = nil)
292
+ aliases = NamespaceMap.new if aliases.nil?
293
+
294
+ ax_args = new_args
295
+
296
+ @data.each{|type_uri, values|
297
+ name = aliases.add(type_uri)
298
+ ax_args['type.'+name] = type_uri
299
+ ax_args['count.'+name] = values.size.to_s
300
+
301
+ values.each_with_index{|value, i|
302
+ key = "value.#{name}.#{i+1}"
303
+ ax_args[key] = value
304
+ }
305
+ }
306
+ return ax_args
307
+ end
308
+
309
+ # Parse attribute exchange key/value arguments into this object.
310
+
311
+ def parse_extension_args(ax_args)
312
+ check_mode(ax_args)
313
+ aliases = NamespaceMap.new
314
+
315
+ ax_args.each{|k, v|
316
+ if k.index('type.') == 0
317
+ type_uri = v
318
+ name = k[5..-1]
319
+
320
+ AX.check_alias(name)
321
+ aliases.add_alias(type_uri,name)
322
+ end
323
+ }
324
+
325
+ aliases.each{|type_uri, name|
326
+ count_s = ax_args['count.'+name]
327
+ count = count_s.to_i
328
+ if count_s.nil?
329
+ value = ax_args['value.'+name]
330
+ if value.nil?
331
+ raise IndexError, "Missing #{'value.'+name} in FetchResponse"
332
+ elsif value.empty?
333
+ values = []
334
+ else
335
+ values = [value]
336
+ end
337
+ elsif count_s.to_i == 0
338
+ values = []
339
+ else
340
+ values = (1..count).inject([]){|l,i|
341
+ key = "value.#{name}.#{i}"
342
+ v = ax_args[key]
343
+ raise IndexError, "Missing #{key} in FetchResponse" if v.nil?
344
+ l << v
345
+ }
346
+ end
347
+ @data[type_uri] = values
348
+ }
349
+ end
350
+
351
+ # Get a single value for an attribute. If no value was sent
352
+ # for this attribute, use the supplied default. If there is more
353
+ # than one value for this attribute, this method will fail.
354
+ def get_single(type_uri, default = nil)
355
+ values = @data[type_uri]
356
+ return default if values.empty?
357
+ if values.size != 1
358
+ raise Error, "More than one value present for #{type_uri.inspect}"
359
+ else
360
+ return values[0]
361
+ end
362
+ end
363
+
364
+ # retrieve the list of values for this attribute
365
+ def get(type_uri)
366
+ @data[type_uri]
367
+ end
368
+
369
+ # retrieve the list of values for this attribute
370
+ def [](type_uri)
371
+ @data[type_uri]
372
+ end
373
+
374
+ # get the number of responses for this attribute
375
+ def count(type_uri)
376
+ @data[type_uri].size
377
+ end
378
+
379
+ end
380
+
381
+ # A fetch_response attribute exchange message
382
+ class FetchResponse < KeyValueMessage
383
+ attr_reader :update_url
384
+
385
+ def initialize(update_url = nil)
386
+ super()
387
+ @mode = 'fetch_response'
388
+ @update_url = update_url
389
+ end
390
+
391
+ # Serialize this object into arguments in the attribute
392
+ # exchange namespace
393
+ # Takes an optional FetchRequest. If specified, the response will be
394
+ # validated against this request, and empty responses for requested
395
+ # fields with no data will be sent.
396
+ def get_extension_args(request = nil)
397
+ aliases = NamespaceMap.new
398
+ zero_value_types = []
399
+
400
+ if request
401
+ # Validate the data in the context of the request (the
402
+ # same attributes should be present in each, and the
403
+ # counts in the response must be no more than the counts
404
+ # in the request)
405
+ @data.keys.each{|type_uri|
406
+ unless request.member? type_uri
407
+ raise IndexError, "Response attribute not present in request: #{type_uri.inspect}"
408
+ end
409
+ }
410
+
411
+ request.attributes.each{|attr_info|
412
+ # Copy the aliases from the request so that reading
413
+ # the response in light of the request is easier
414
+ if attr_info.ns_alias.nil?
415
+ aliases.add(attr_info.type_uri)
416
+ else
417
+ aliases.add_alias(attr_info.type_uri, attr_info.ns_alias)
418
+ end
419
+ values = @data[attr_info.type_uri]
420
+ if values.empty? # @data defaults to []
421
+ zero_value_types << attr_info
422
+ end
423
+ if attr_info.count != UNLIMITED_VALUES and attr_info.count < values.size
424
+ raise Error, "More than the number of requested values were specified for #{attr_info.type_uri.inspect}"
425
+ end
426
+ }
427
+ end
428
+
429
+ kv_args = _get_extension_kv_args(aliases)
430
+
431
+ # Add the KV args into the response with the args that are
432
+ # unique to the fetch_response
433
+ ax_args = new_args
434
+
435
+ zero_value_types.each{|attr_info|
436
+ name = aliases.get_alias(attr_info.type_uri)
437
+ kv_args['type.' + name] = attr_info.type_uri
438
+ kv_args['count.' + name] = '0'
439
+ }
440
+ update_url = (request and request.update_url or @update_url)
441
+ ax_args['update_url'] = update_url unless update_url.nil?
442
+ ax_args.update(kv_args)
443
+ return ax_args
444
+ end
445
+
446
+ def parse_extension_args(ax_args)
447
+ super
448
+ @update_url = ax_args['update_url']
449
+ end
450
+
451
+ # Construct a FetchResponse object from an OpenID library
452
+ # SuccessResponse object.
453
+ def self.from_success_response(success_response, signed=true)
454
+ obj = self.new
455
+ if signed
456
+ ax_args = success_response.get_signed_ns(obj.ns_uri)
457
+ else
458
+ ax_args = success_response.message.get_args(obj.ns_uri)
459
+ end
460
+
461
+ begin
462
+ obj.parse_extension_args(ax_args)
463
+ return obj
464
+ rescue Error => e
465
+ return nil
466
+ end
467
+ end
468
+ end
469
+
470
+ # A store request attribute exchange message representation
471
+ class StoreRequest < KeyValueMessage
472
+
473
+ MODE = 'store_request'
474
+
475
+ def initialize
476
+ super
477
+ @mode = MODE
478
+ end
479
+
480
+ # Extract a StoreRequest from an OpenID message
481
+ # message: OpenID::Message
482
+ # return a StoreRequest or nil if AX arguments are not present
483
+ def self.from_openid_request(oidreq)
484
+ message = oidreq.message
485
+ ax_args = message.get_args(NS_URI)
486
+ return nil if ax_args.empty? or ax_args['mode'] != MODE
487
+ req = new
488
+ req.parse_extension_args(ax_args)
489
+ req
490
+ end
491
+
492
+ def get_extension_args(aliases=nil)
493
+ ax_args = new_args
494
+ kv_args = _get_extension_kv_args(aliases)
495
+ ax_args.update(kv_args)
496
+ return ax_args
497
+ end
498
+ end
499
+
500
+ # An indication that the store request was processed along with
501
+ # this OpenID transaction.
502
+ class StoreResponse < AXMessage
503
+ SUCCESS_MODE = 'store_response_success'
504
+ FAILURE_MODE = 'store_response_failure'
505
+ attr_reader :error_message
506
+
507
+ def initialize(succeeded = true, error_message = nil)
508
+ super()
509
+ if succeeded and error_message
510
+ raise Error, "Error message included in a success response"
511
+ end
512
+ if succeeded
513
+ @mode = SUCCESS_MODE
514
+ else
515
+ @mode = FAILURE_MODE
516
+ end
517
+ @error_message = error_message
518
+ end
519
+
520
+ def self.from_success_response(success_response)
521
+ resp = nil
522
+ ax_args = success_response.message.get_args(NS_URI)
523
+ resp = ax_args.key?('error') ? new(false, ax_args['error']) : new
524
+ end
525
+
526
+ def succeeded?
527
+ @mode == SUCCESS_MODE
528
+ end
529
+
530
+ def get_extension_args
531
+ ax_args = new_args
532
+ if !succeeded? and error_message
533
+ ax_args['error'] = @error_message
534
+ end
535
+ return ax_args
536
+ end
537
+ end
538
+ end
539
+ end