ruby-openid 1.1.4 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ruby-openid might be problematic. Click here for more details.

Files changed (207) hide show
  1. data/INSTALL +0 -9
  2. data/README +21 -22
  3. data/UPGRADE +117 -0
  4. data/admin/runtests.rb +36 -0
  5. data/examples/README +13 -21
  6. data/examples/active_record_openid_store/README +8 -3
  7. data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +4 -8
  8. data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
  9. data/examples/active_record_openid_store/lib/association.rb +2 -0
  10. data/examples/active_record_openid_store/lib/openid_ar_store.rb +22 -47
  11. data/examples/active_record_openid_store/test/store_test.rb +78 -48
  12. data/examples/discover +46 -0
  13. data/examples/{rails_server → rails_openid}/README +0 -0
  14. data/examples/{rails_server → rails_openid}/Rakefile +0 -0
  15. data/examples/{rails_server → rails_openid}/app/controllers/application.rb +0 -0
  16. data/examples/rails_openid/app/controllers/consumer_controller.rb +115 -0
  17. data/examples/{rails_server → rails_openid}/app/controllers/login_controller.rb +10 -2
  18. data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
  19. data/examples/{rails_server → rails_openid}/app/helpers/application_helper.rb +0 -0
  20. data/examples/{rails_server → rails_openid}/app/helpers/login_helper.rb +0 -0
  21. data/examples/{rails_server → rails_openid}/app/helpers/server_helper.rb +0 -0
  22. data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
  23. data/examples/rails_openid/app/views/consumer/start.rhtml +8 -0
  24. data/examples/{rails_server → rails_openid}/app/views/layouts/server.rhtml +0 -0
  25. data/examples/{rails_server → rails_openid}/app/views/login/index.rhtml +1 -1
  26. data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
  27. data/examples/{rails_server → rails_openid}/config/boot.rb +0 -0
  28. data/examples/{rails_server → rails_openid}/config/database.yml +0 -0
  29. data/examples/{rails_server → rails_openid}/config/environment.rb +0 -0
  30. data/examples/{rails_server → rails_openid}/config/environments/development.rb +0 -0
  31. data/examples/{rails_server → rails_openid}/config/environments/production.rb +0 -0
  32. data/examples/{rails_server → rails_openid}/config/environments/test.rb +0 -0
  33. data/examples/{rails_server → rails_openid}/config/routes.rb +2 -1
  34. data/examples/{rails_server → rails_openid}/doc/README_FOR_APP +0 -0
  35. data/examples/{rails_server → rails_openid}/public/404.html +0 -0
  36. data/examples/{rails_server → rails_openid}/public/500.html +0 -0
  37. data/examples/{rails_server → rails_openid}/public/dispatch.cgi +0 -0
  38. data/examples/{rails_server → rails_openid}/public/dispatch.fcgi +0 -0
  39. data/examples/{rails_server → rails_openid}/public/dispatch.rb +0 -0
  40. data/examples/{rails_server → rails_openid}/public/favicon.ico +0 -0
  41. data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
  42. data/examples/{rails_server → rails_openid}/public/javascripts/controls.js +0 -0
  43. data/examples/{rails_server → rails_openid}/public/javascripts/dragdrop.js +0 -0
  44. data/examples/{rails_server → rails_openid}/public/javascripts/effects.js +0 -0
  45. data/examples/{rails_server → rails_openid}/public/javascripts/prototype.js +0 -0
  46. data/examples/{rails_server → rails_openid}/public/robots.txt +0 -0
  47. data/examples/{rails_server → rails_openid}/script/about +0 -0
  48. data/examples/{rails_server → rails_openid}/script/breakpointer +0 -0
  49. data/examples/{rails_server → rails_openid}/script/console +0 -0
  50. data/examples/{rails_server → rails_openid}/script/destroy +0 -0
  51. data/examples/{rails_server → rails_openid}/script/generate +0 -0
  52. data/examples/{rails_server → rails_openid}/script/performance/benchmarker +0 -0
  53. data/examples/{rails_server → rails_openid}/script/performance/profiler +0 -0
  54. data/examples/{rails_server → rails_openid}/script/plugin +0 -0
  55. data/examples/{rails_server → rails_openid}/script/process/reaper +0 -0
  56. data/examples/{rails_server → rails_openid}/script/process/spawner +0 -0
  57. data/examples/{rails_server → rails_openid}/script/process/spinner +0 -0
  58. data/examples/{rails_server → rails_openid}/script/runner +0 -0
  59. data/examples/{rails_server → rails_openid}/script/server +0 -0
  60. data/examples/{rails_server → rails_openid}/test/functional/login_controller_test.rb +0 -0
  61. data/examples/{rails_server → rails_openid}/test/functional/server_controller_test.rb +0 -0
  62. data/examples/{rails_server → rails_openid}/test/test_helper.rb +0 -0
  63. data/lib/{hmac.rb → hmac/hmac.rb} +0 -0
  64. data/lib/{hmac-sha1.rb → hmac/sha1.rb} +1 -1
  65. data/lib/{hmac-sha2.rb → hmac/sha2.rb} +1 -1
  66. data/lib/openid/association.rb +213 -73
  67. data/lib/openid/consumer/associationmanager.rb +338 -0
  68. data/lib/openid/consumer/checkid_request.rb +175 -0
  69. data/lib/openid/consumer/discovery.rb +480 -0
  70. data/lib/openid/consumer/discovery_manager.rb +123 -0
  71. data/lib/openid/consumer/html_parse.rb +136 -0
  72. data/lib/openid/consumer/idres.rb +525 -0
  73. data/lib/openid/consumer/responses.rb +133 -0
  74. data/lib/openid/consumer.rb +280 -807
  75. data/lib/openid/cryptutil.rb +85 -0
  76. data/lib/openid/dh.rb +60 -23
  77. data/lib/openid/extension.rb +31 -0
  78. data/lib/openid/extensions/ax.rb +506 -0
  79. data/lib/openid/extensions/pape.rb +182 -0
  80. data/lib/openid/extensions/sreg.rb +275 -0
  81. data/lib/openid/extras.rb +11 -0
  82. data/lib/openid/fetchers.rb +132 -93
  83. data/lib/openid/kvform.rb +133 -0
  84. data/lib/openid/kvpost.rb +56 -0
  85. data/lib/openid/message.rb +534 -0
  86. data/lib/openid/protocolerror.rb +6 -0
  87. data/lib/openid/server.rb +1215 -666
  88. data/lib/openid/store/filesystem.rb +271 -0
  89. data/lib/openid/store/interface.rb +75 -0
  90. data/lib/openid/store/memory.rb +84 -0
  91. data/lib/openid/store/nonce.rb +68 -0
  92. data/lib/openid/trustroot.rb +314 -87
  93. data/lib/openid/urinorm.rb +37 -34
  94. data/lib/openid/util.rb +42 -220
  95. data/lib/openid/yadis/accept.rb +148 -0
  96. data/lib/openid/yadis/constants.rb +21 -0
  97. data/lib/openid/yadis/discovery.rb +153 -0
  98. data/lib/openid/yadis/filters.rb +205 -0
  99. data/lib/openid/{htmltokenizer.rb → yadis/htmltokenizer.rb} +1 -54
  100. data/lib/openid/yadis/parsehtml.rb +36 -0
  101. data/lib/openid/yadis/services.rb +42 -0
  102. data/lib/openid/yadis/xrds.rb +171 -0
  103. data/lib/openid/yadis/xri.rb +90 -0
  104. data/lib/openid/yadis/xrires.rb +106 -0
  105. data/lib/openid.rb +1 -4
  106. data/test/data/accept.txt +124 -0
  107. data/test/data/dh.txt +29 -0
  108. data/test/data/example-xrds.xml +14 -0
  109. data/test/data/linkparse.txt +587 -0
  110. data/test/data/n2b64 +650 -0
  111. data/test/data/test1-discover.txt +137 -0
  112. data/test/data/test1-parsehtml.txt +128 -0
  113. data/test/data/test_discover/openid.html +11 -0
  114. data/test/data/test_discover/openid2.html +11 -0
  115. data/test/data/test_discover/openid2_xrds.xml +12 -0
  116. data/test/data/test_discover/openid2_xrds_no_local_id.xml +11 -0
  117. data/test/data/test_discover/openid_1_and_2.html +11 -0
  118. data/test/data/test_discover/openid_1_and_2_xrds.xml +16 -0
  119. data/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
  120. data/test/data/test_discover/openid_and_yadis.html +12 -0
  121. data/test/data/test_discover/openid_no_delegate.html +10 -0
  122. data/test/data/test_discover/yadis_0entries.xml +12 -0
  123. data/test/data/test_discover/yadis_2_bad_local_id.xml +15 -0
  124. data/test/data/test_discover/yadis_2entries_delegate.xml +22 -0
  125. data/test/data/test_discover/yadis_2entries_idp.xml +21 -0
  126. data/test/data/test_discover/yadis_another_delegate.xml +14 -0
  127. data/test/data/test_discover/yadis_idp.xml +12 -0
  128. data/test/data/test_discover/yadis_idp_delegate.xml +13 -0
  129. data/test/data/test_discover/yadis_no_delegate.xml +11 -0
  130. data/test/data/test_xrds/=j3h.2007.11.14.xrds +25 -0
  131. data/test/data/test_xrds/README +12 -0
  132. data/test/data/test_xrds/delegated-20060809-r1.xrds +34 -0
  133. data/test/data/test_xrds/delegated-20060809-r2.xrds +34 -0
  134. data/test/data/test_xrds/delegated-20060809.xrds +34 -0
  135. data/test/data/test_xrds/no-xrd.xml +7 -0
  136. data/test/data/test_xrds/not-xrds.xml +2 -0
  137. data/test/data/test_xrds/prefixsometimes.xrds +34 -0
  138. data/test/data/test_xrds/ref.xrds +109 -0
  139. data/test/data/test_xrds/sometimesprefix.xrds +34 -0
  140. data/test/data/test_xrds/spoof1.xrds +25 -0
  141. data/test/data/test_xrds/spoof2.xrds +25 -0
  142. data/test/data/test_xrds/spoof3.xrds +37 -0
  143. data/test/data/test_xrds/status222.xrds +9 -0
  144. data/test/data/test_xrds/valid-populated-xrds.xml +39 -0
  145. data/test/data/trustroot.txt +147 -0
  146. data/test/discoverdata.rb +131 -0
  147. data/test/test_accept.rb +170 -0
  148. data/test/test_association.rb +266 -0
  149. data/test/test_associationmanager.rb +899 -0
  150. data/test/test_ax.rb +587 -0
  151. data/test/test_checkid_request.rb +297 -0
  152. data/test/test_consumer.rb +257 -0
  153. data/test/test_cryptutil.rb +117 -0
  154. data/test/test_dh.rb +86 -0
  155. data/test/test_discover.rb +772 -0
  156. data/test/test_discovery_manager.rb +262 -0
  157. data/test/test_extras.rb +35 -0
  158. data/test/test_fetchers.rb +472 -0
  159. data/test/test_filters.rb +270 -0
  160. data/test/test_idres.rb +816 -0
  161. data/test/test_kvform.rb +165 -0
  162. data/test/test_kvpost.rb +65 -0
  163. data/test/test_linkparse.rb +101 -0
  164. data/test/test_message.rb +1058 -0
  165. data/test/test_nonce.rb +89 -0
  166. data/test/test_openid_yadis.rb +178 -0
  167. data/test/test_pape.rb +233 -0
  168. data/test/test_parsehtml.rb +80 -0
  169. data/test/test_responses.rb +63 -0
  170. data/test/test_server.rb +2270 -0
  171. data/test/test_sreg.rb +479 -0
  172. data/test/test_stores.rb +269 -0
  173. data/test/test_trustroot.rb +112 -0
  174. data/test/{urinorm.rb → test_urinorm.rb} +6 -3
  175. data/test/test_util.rb +144 -0
  176. data/test/test_xrds.rb +160 -0
  177. data/test/test_xri.rb +48 -0
  178. data/test/test_xrires.rb +63 -0
  179. data/test/test_yadis_discovery.rb +207 -0
  180. data/test/testutil.rb +116 -0
  181. data/test/util.rb +47 -50
  182. metadata +233 -143
  183. data/examples/consumer.rb +0 -290
  184. data/examples/rails_openid_login_generator/openid_login_generator-0.1.gem +0 -0
  185. data/examples/rails_server/app/controllers/server_controller.rb +0 -190
  186. data/examples/rails_server/app/views/server/decide.rhtml +0 -11
  187. data/examples/rails_server/public/images/rails.png +0 -0
  188. data/lib/hmac-md5.rb +0 -11
  189. data/lib/hmac-rmd160.rb +0 -11
  190. data/lib/openid/discovery.rb +0 -122
  191. data/lib/openid/filestore.rb +0 -315
  192. data/lib/openid/parse.rb +0 -23
  193. data/lib/openid/service.rb +0 -147
  194. data/lib/openid/stores.rb +0 -178
  195. data/test/assoc.rb +0 -38
  196. data/test/consumer.rb +0 -376
  197. data/test/data/brian.xrds +0 -16
  198. data/test/data/brianellin.mylid.xrds +0 -42
  199. data/test/dh.rb +0 -20
  200. data/test/extensions.rb +0 -30
  201. data/test/linkparse.rb +0 -305
  202. data/test/runtests.rb +0 -22
  203. data/test/server2.rb +0 -1053
  204. data/test/service.rb +0 -47
  205. data/test/storetestcase.rb +0 -172
  206. data/test/teststore.rb +0 -47
  207. data/test/trustroot.rb +0 -117
@@ -0,0 +1,56 @@
1
+ require "openid/message"
2
+ require "openid/fetchers"
3
+
4
+ module OpenID
5
+ # Exception that is raised when the server returns a 400 response
6
+ # code to a direct request.
7
+ class ServerError < Exception
8
+ attr_reader :error_text, :error_code, :message
9
+
10
+ def initialize(error_text, error_code, message)
11
+ super(error_text)
12
+ @error_text = error_text
13
+ @error_code = error_code
14
+ @message = message
15
+ end
16
+
17
+ def self.from_message(msg)
18
+ error_text = msg.get_arg(OPENID_NS, 'error',
19
+ '<no error message supplied>')
20
+ error_code = msg.get_arg(OPENID_NS, 'error_code')
21
+ return self.new(error_text, error_code, msg)
22
+ end
23
+ end
24
+
25
+ class KVPostNetworkError < OpenIDError
26
+ end
27
+ class HTTPStatusError < OpenIDError
28
+ end
29
+
30
+ class Message
31
+ def self.from_http_response(response, server_url)
32
+ msg = self.from_kvform(response.body)
33
+ case response.code.to_i
34
+ when 200
35
+ return msg
36
+ when 400
37
+ raise ServerError.from_message(msg)
38
+ else
39
+ error_message = "bad status code from server #{server_url}: "\
40
+ "#{response.code}"
41
+ raise HTTPStatusError.new(error_message)
42
+ end
43
+ end
44
+ end
45
+
46
+ # Send the message to the server via HTTP POST and receive and parse
47
+ # a response in KV Form
48
+ def self.make_kv_post(request_message, server_url)
49
+ begin
50
+ http_response = self.fetch(server_url, request_message.to_url_encoded)
51
+ rescue Exception
52
+ raise KVPostNetworkError.new("Unable to contact OpenID server: #{$!.to_s}")
53
+ end
54
+ return Message.from_http_response(http_response, server_url)
55
+ end
56
+ end
@@ -0,0 +1,534 @@
1
+ require 'openid/util'
2
+ require 'openid/kvform'
3
+
4
+ module OpenID
5
+
6
+ IDENTIFIER_SELECT = 'http://specs.openid.net/auth/2.0/identifier_select'
7
+
8
+ # URI for Simple Registration extension, the only commonly deployed
9
+ # OpenID 1.x extension, and so a special case.
10
+ SREG_URI = 'http://openid.net/sreg/1.0'
11
+
12
+ # The OpenID 1.x namespace URI
13
+ OPENID1_NS = 'http://openid.net/signon/1.0'
14
+
15
+ # The OpenID 2.0 namespace URI
16
+ OPENID2_NS = 'http://specs.openid.net/auth/2.0'
17
+
18
+ # The namespace consisting of pairs with keys that are prefixed with
19
+ # "openid." but not in another namespace.
20
+ NULL_NAMESPACE = :null_namespace
21
+
22
+ # The null namespace, when it is an allowed OpenID namespace
23
+ OPENID_NS = :openid_namespace
24
+
25
+ # The top-level namespace, excluding all pairs with keys that start
26
+ # with "openid."
27
+ BARE_NS = :bare_namespace
28
+
29
+ # Limit, in bytes, of identity provider and return_to URLs,
30
+ # including response payload. See OpenID 1.1 specification,
31
+ # Appendix D.
32
+ OPENID1_URL_LIMIT = 2047
33
+
34
+ # All OpenID protocol fields. Used to check namespace aliases.
35
+ OPENID_PROTOCOL_FIELDS = [
36
+ 'ns', 'mode', 'error', 'return_to',
37
+ 'contact', 'reference', 'signed',
38
+ 'assoc_type', 'session_type',
39
+ 'dh_modulus', 'dh_gen',
40
+ 'dh_consumer_public', 'claimed_id',
41
+ 'identity', 'realm', 'invalidate_handle',
42
+ 'op_endpoint', 'response_nonce', 'sig',
43
+ 'assoc_handle', 'trust_root', 'openid',
44
+ ]
45
+
46
+ # Sentinel used for Message implementation to indicate that getArg
47
+ # should raise an exception instead of returning a default.
48
+ NO_DEFAULT = :no_default
49
+
50
+ # Raised if the generic OpenID namespace is accessed when there
51
+ # is no OpenID namespace set for this message.
52
+ class UndefinedOpenIDNamespace < Exception; end
53
+
54
+ # Raised when an alias or namespace URI has already been registered.
55
+ class NamespaceAliasRegistrationError < Exception; end
56
+
57
+ class Message
58
+ attr_reader :namespaces
59
+
60
+ # Raised when key lookup fails
61
+ class KeyNotFound < IndexError ; end
62
+
63
+ # Namespace / alias registration map. See
64
+ # register_namespace_alias.
65
+ @@registered_aliases = {}
66
+
67
+ # Registers a (namespace URI, alias) mapping in a global namespace
68
+ # alias map. Raises NamespaceAliasRegistrationError if either the
69
+ # namespace URI or alias has already been registered with a
70
+ # different value. This function is required if you want to use a
71
+ # namespace with an OpenID 1 message.
72
+ def Message.register_namespace_alias(namespace_uri, alias_)
73
+ if @@registered_aliases[alias_] == namespace_uri
74
+ return
75
+ end
76
+
77
+ if @@registered_aliases.values.include?(namespace_uri)
78
+ raise NamespaceAliasRegistrationError,
79
+ 'Namespace uri #{namespace_uri} already registered'
80
+ end
81
+
82
+ if @@registered_aliases.member?(alias_)
83
+ raise NamespaceAliasRegistrationError,
84
+ 'Alias #{alias_} already registered'
85
+ end
86
+
87
+ @@registered_aliases[alias_] = namespace_uri
88
+ end
89
+
90
+ @@allowed_openid_namespaces = [OPENID1_NS, OPENID2_NS]
91
+
92
+ def initialize(openid_namspace=nil)
93
+ @args = {}
94
+ @namespaces = NamespaceMap.new
95
+ if openid_namspace
96
+ self.set_openid_namespace(openid_namspace)
97
+ else
98
+ @openid_ns_uri = nil
99
+ end
100
+ end
101
+
102
+ # Construct a Message containing a set of POST arguments.
103
+ def Message.from_post_args(args)
104
+ m = Message.new
105
+ openid_args = {}
106
+ args.each do |key,value|
107
+ if value.is_a?(Array)
108
+ raise ArgumentError, "Query dict must have one value for each key, " +
109
+ "not lists of values. Query is #{args.inspect}"
110
+ end
111
+
112
+ prefix, rest = key.split('.', 2)
113
+
114
+ if prefix != 'openid' or rest.nil?
115
+ m.set_arg(BARE_NS, key, value)
116
+ else
117
+ openid_args[rest] = value
118
+ end
119
+ end
120
+
121
+ m._from_openid_args(openid_args)
122
+ return m
123
+ end
124
+
125
+ # Construct a Message from a parsed KVForm message.
126
+ def Message.from_openid_args(openid_args)
127
+ m = Message.new
128
+ m._from_openid_args(openid_args)
129
+ return m
130
+ end
131
+
132
+ def _from_openid_args(openid_args)
133
+ ns_args = []
134
+
135
+ # resolve namespaces
136
+ openid_args.each { |rest, value|
137
+ ns_alias, ns_key = rest.split('.', 2)
138
+ if ns_key.nil?
139
+ ns_alias = NULL_NAMESPACE
140
+ ns_key = rest
141
+ end
142
+
143
+ if ns_alias == 'ns'
144
+ @namespaces.add_alias(value, ns_key)
145
+ elsif ns_alias == NULL_NAMESPACE and ns_key == 'ns'
146
+ @namespaces.add_alias(value, NULL_NAMESPACE)
147
+ else
148
+ ns_args << [ns_alias, ns_key, value]
149
+ end
150
+ }
151
+
152
+ # ensure that there is an OpenID namespace definition
153
+ openid_ns_uri = @namespaces.get_namespace_uri(NULL_NAMESPACE)
154
+ openid_ns_uri = OPENID1_NS unless openid_ns_uri
155
+
156
+ self.set_openid_namespace(openid_ns_uri)
157
+
158
+ # put the pairs into the appropriate namespaces
159
+ ns_args.each { |ns_alias, ns_key, value|
160
+ ns_uri = @namespaces.get_namespace_uri(ns_alias)
161
+ unless ns_uri
162
+ # only try to map an alias to a default if it's an
163
+ # OpenID 1.x namespace
164
+ @@registered_aliases.each { |_alias, _uri|
165
+ if _alias == ns_alias
166
+ ns_uri = _uri
167
+ break
168
+ end
169
+ }
170
+
171
+ unless ns_uri
172
+ ns_uri = openid_ns_uri
173
+ ns_key = "#{ns_alias}.#{ns_key}"
174
+ else
175
+ @namespaces.add_alias(ns_uri, ns_alias)
176
+ end
177
+ end
178
+ self.set_arg(ns_uri, ns_key, value)
179
+ }
180
+ end
181
+
182
+ def set_openid_namespace(openid_ns_uri)
183
+ if !@@allowed_openid_namespaces.include?(openid_ns_uri)
184
+ raise ArgumentError, "Invalid null namespace: #{openid_ns_uri}"
185
+ end
186
+ @namespaces.add_alias(openid_ns_uri, NULL_NAMESPACE)
187
+ @openid_ns_uri = openid_ns_uri
188
+ end
189
+
190
+ def get_openid_namespace
191
+ return @openid_ns_uri
192
+ end
193
+
194
+ def is_openid1
195
+ return @openid_ns_uri == OPENID1_NS
196
+ end
197
+
198
+ def is_openid2
199
+ return @openid_ns_uri == OPENID2_NS
200
+ end
201
+
202
+ # Create a message from a KVForm string
203
+ def Message.from_kvform(kvform_string)
204
+ return Message.from_openid_args(Util.kv_to_dict(kvform_string))
205
+ end
206
+
207
+ def copy
208
+ return Marshal.load(Marshal.dump(self))
209
+ end
210
+
211
+ # Return all arguments with "openid." in from of namespaced arguments.
212
+ def to_post_args
213
+ args = {}
214
+
215
+ # add namespace defs to the output
216
+ @namespaces.each { |ns_uri, ns_alias|
217
+ if ns_alias == NULL_NAMESPACE
218
+ if ns_uri != OPENID1_NS
219
+ args['openid.ns'] = ns_uri
220
+ end
221
+ else
222
+ if get_openid_namespace != OPENID1_NS
223
+ ns_key = 'openid.ns.' + ns_alias
224
+ args[ns_key] = ns_uri
225
+ end
226
+ end
227
+ }
228
+
229
+ @args.each { |k, value|
230
+ ns_uri, ns_key = k
231
+ key = get_key(ns_uri, ns_key)
232
+ args[key] = value
233
+ }
234
+
235
+ return args
236
+ end
237
+
238
+ # Return all namespaced arguments, failing if any non-namespaced arguments
239
+ # exist.
240
+ def to_args
241
+ post_args = self.to_post_args
242
+ kvargs = {}
243
+ post_args.each { |k,v|
244
+ if !k.starts_with?('openid.')
245
+ raise ArgumentError, "This message can only be encoded as a POST, because it contains arguments that are not prefixed with 'openid.'"
246
+ else
247
+ kvargs[k[7..-1]] = v
248
+ end
249
+ }
250
+ return kvargs
251
+ end
252
+
253
+ # Generate HTML form markup that contains the values in this
254
+ # message, to be HTTP POSTed as x-www-form-urlencoded UTF-8.
255
+ def to_form_markup(action_url, form_tag_attrs=nil, submit_text='Continue')
256
+ form_tag_attr_map = {}
257
+
258
+ if form_tag_attrs
259
+ form_tag_attrs.each { |name, attr|
260
+ form_tag_attr_map[name] = attr
261
+ }
262
+ end
263
+
264
+ form_tag_attr_map['action'] = action_url
265
+ form_tag_attr_map['method'] = 'post'
266
+ form_tag_attr_map['accept-charset'] = 'UTF-8'
267
+ form_tag_attr_map['enctype'] = 'application/x-www-form-urlencoded'
268
+
269
+ markup = "<form "
270
+
271
+ form_tag_attr_map.each { |k, v|
272
+ markup += " #{k}=\"#{v}\""
273
+ }
274
+
275
+ markup += ">\n"
276
+
277
+ to_post_args.each { |k,v|
278
+ markup += "<input type='hidden' name='#{k}' value='#{v}' />\n"
279
+ }
280
+ markup += "<input type='submit' value='#{submit_text}' />\n"
281
+ markup += "\n</form>"
282
+ return markup
283
+ end
284
+
285
+ # Generate a GET URL with the paramters in this message attacked as
286
+ # query parameters.
287
+ def to_url(base_url)
288
+ return Util.append_args(base_url, self.to_post_args)
289
+ end
290
+
291
+ # Generate a KVForm string that contains the parameters in this message.
292
+ # This will fail is the message contains arguments outside of the
293
+ # "openid." prefix.
294
+ def to_kvform
295
+ return Util.dict_to_kv(to_args)
296
+ end
297
+
298
+ # Generate an x-www-urlencoded string.
299
+ def to_url_encoded
300
+ args = to_post_args.map.sort
301
+ return Util.urlencode(args)
302
+ end
303
+
304
+ # Convert an input value into the internally used values of this obejct.
305
+ def _fix_ns(namespace)
306
+ if namespace == OPENID_NS
307
+ unless @openid_ns_uri
308
+ raise UndefinedOpenIDNamespace, 'OpenID namespace not set'
309
+ else
310
+ namespace = @openid_ns_uri
311
+ end
312
+ end
313
+
314
+ if namespace == BARE_NS
315
+ return namespace
316
+ end
317
+
318
+ if !namespace.is_a?(String)
319
+ raise ArgumentError, ("Namespace must be BARE_NS, OPENID_NS or "\
320
+ "a string. Got #{namespace.inspect}")
321
+ end
322
+
323
+ if namespace.index(':').nil?
324
+ msg = ("OpenID 2.0 namespace identifiers SHOULD be URIs. "\
325
+ "Got #{namespace.inspect}")
326
+ Util.log(msg)
327
+
328
+ if namespace == 'sreg'
329
+ msg = "Using #{SREG_URI} instead of \"sreg\" as namespace"
330
+ Util.log(msg)
331
+ return SREG_URI
332
+ end
333
+ end
334
+
335
+ return namespace
336
+ end
337
+
338
+ def has_key?(namespace, ns_key)
339
+ namespace = _fix_ns(namespace)
340
+ return @args.member?([namespace, ns_key])
341
+ end
342
+
343
+ # Get the key for a particular namespaced argument
344
+ def get_key(namespace, ns_key)
345
+ namespace = _fix_ns(namespace)
346
+ return ns_key if namespace == BARE_NS
347
+
348
+ ns_alias = @namespaces.get_alias(namespace)
349
+
350
+ # no alias is defined, so no key can exist
351
+ return nil if ns_alias.nil?
352
+
353
+ if ns_alias == NULL_NAMESPACE
354
+ tail = ns_key
355
+ else
356
+ tail = "#{ns_alias}.#{ns_key}"
357
+ end
358
+
359
+ return 'openid.' + tail
360
+ end
361
+
362
+ # Get a value for a namespaced key.
363
+ def get_arg(namespace, key, default=nil)
364
+ namespace = _fix_ns(namespace)
365
+ @args.fetch([namespace, key]) {
366
+ if default == NO_DEFAULT
367
+ raise KeyNotFound, "<#{namespace}>#{key} not in this message"
368
+ else
369
+ default
370
+ end
371
+ }
372
+ end
373
+
374
+ # Get the arguments that are defined for this namespace URI.
375
+ def get_args(namespace)
376
+ namespace = _fix_ns(namespace)
377
+ args = {}
378
+ @args.each { |k,v|
379
+ pair_ns, ns_key = k
380
+ args[ns_key] = v if pair_ns == namespace
381
+ }
382
+ return args
383
+ end
384
+
385
+ # Set multiple key/value pairs in one call.
386
+ def update_args(namespace, updates)
387
+ namespace = _fix_ns(namespace)
388
+ updates.each {|k,v| set_arg(namespace, k, v)}
389
+ end
390
+
391
+ # Set a single argument in this namespace
392
+ def set_arg(namespace, key, value)
393
+ namespace = _fix_ns(namespace)
394
+ @args[[namespace, key].freeze] = value
395
+ if namespace != BARE_NS
396
+ @namespaces.add(namespace)
397
+ end
398
+ end
399
+
400
+ # Remove a single argument from this namespace.
401
+ def del_arg(namespace, key)
402
+ namespace = _fix_ns(namespace)
403
+ _key = [namespace, key]
404
+ @args.delete(_key)
405
+ end
406
+
407
+ def ==(other)
408
+ other.is_a?(self.class) && @args == other.instance_eval { @args }
409
+ end
410
+
411
+ def get_aliased_arg(aliased_key, default=nil)
412
+ if aliased_key == 'ns'
413
+ return get_openid_namespace()
414
+ end
415
+
416
+ ns_alias, key = aliased_key.split('.', 2)
417
+ if ns_alias == 'ns'
418
+ uri = @namespaces.get_namespace_uri(key)
419
+ if uri.nil? and default == NO_DEFAULT
420
+ raise KeyNotFound, "Namespace #{key} not defined when looking "\
421
+ "for #{aliased_key}"
422
+ else
423
+ return (uri.nil? ? default : uri)
424
+ end
425
+ end
426
+
427
+ if key.nil?
428
+ key = aliased_key
429
+ ns = nil
430
+ else
431
+ ns = @namespaces.get_namespace_uri(ns_alias)
432
+ end
433
+
434
+ if ns.nil?
435
+ key = aliased_key
436
+ ns = get_openid_namespace
437
+ end
438
+
439
+ return get_arg(ns, key, default)
440
+ end
441
+ end
442
+
443
+
444
+ # Maintains a bidirectional map between namespace URIs and aliases.
445
+ class NamespaceMap
446
+
447
+ def initialize
448
+ @alias_to_namespace = {}
449
+ @namespace_to_alias = {}
450
+ end
451
+
452
+ def get_alias(namespace_uri)
453
+ @namespace_to_alias[namespace_uri]
454
+ end
455
+
456
+ def get_namespace_uri(namespace_alias)
457
+ @alias_to_namespace[namespace_alias]
458
+ end
459
+
460
+ # Add an alias from this namespace URI to the alias.
461
+ def add_alias(namespace_uri, desired_alias)
462
+ # Check that desired_alias is not an openid protocol field as
463
+ # per the spec.
464
+ Util.assert(!OPENID_PROTOCOL_FIELDS.include?(desired_alias),
465
+ "#{desired_alias} is not an allowed namespace alias")
466
+
467
+ # check that there is not a namespace already defined for the
468
+ # desired alias
469
+ current_namespace_uri = @alias_to_namespace.fetch(desired_alias, nil)
470
+ if current_namespace_uri and current_namespace_uri != namespace_uri
471
+ raise IndexError, "Cannot map #{namespace_uri} to alias #{desired_alias}. #{current_namespace_uri} is already mapped to alias #{desired_alias}"
472
+ end
473
+
474
+ # Check that desired_alias does not contain a period as per the
475
+ # spec.
476
+ if desired_alias.is_a?(String)
477
+ Util.assert(desired_alias.index('.').nil?,
478
+ "#{desired_alias} must not contain a dot")
479
+ end
480
+
481
+ # check that there is not already a (different) alias for this
482
+ # namespace URI.
483
+ _alias = @namespace_to_alias[namespace_uri]
484
+ if _alias and _alias != desired_alias
485
+ raise IndexError, "Cannot map #{namespace_uri} to alias #{desired_alias}. It is already mapped to alias #{_alias}"
486
+ end
487
+
488
+ @alias_to_namespace[desired_alias] = namespace_uri
489
+ @namespace_to_alias[namespace_uri] = desired_alias
490
+ return desired_alias
491
+ end
492
+
493
+ # Add this namespace URI to the mapping, without caring what alias
494
+ # it ends up with.
495
+ def add(namespace_uri)
496
+ # see if this namepace is already mapped to an alias
497
+ _alias = @namespace_to_alias[namespace_uri]
498
+ return _alias if _alias
499
+
500
+ # Fall back to generating a numberical alias
501
+ i = 0
502
+ while true
503
+ _alias = 'ext' + i.to_s
504
+ begin
505
+ add_alias(namespace_uri, _alias)
506
+ rescue IndexError
507
+ i += 1
508
+ else
509
+ return _alias
510
+ end
511
+ end
512
+
513
+ raise StandardError, 'Unreachable'
514
+ end
515
+
516
+ def member?(namespace_uri)
517
+ @namespace_to_alias.has_key?(namespace_uri)
518
+ end
519
+
520
+ def each
521
+ @namespace_to_alias.each {|k,v| yield k,v}
522
+ end
523
+
524
+ def namespace_uris
525
+ # Return an iterator over the namespace URIs
526
+ return @namespace_to_alias.keys()
527
+ end
528
+
529
+ def aliases
530
+ # Return an iterator over the aliases
531
+ return @alias_to_namespace.keys()
532
+ end
533
+ end
534
+ end
@@ -0,0 +1,6 @@
1
+ module OpenID
2
+
3
+ # An error in the OpenID protocol
4
+ class ProtocolError < Exception
5
+ end
6
+ end