ruby-openid 1.1.4 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. data/INSTALL +0 -9
  2. data/README +21 -22
  3. data/UPGRADE +117 -0
  4. data/admin/runtests.rb +36 -0
  5. data/examples/README +13 -21
  6. data/examples/active_record_openid_store/README +8 -3
  7. data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +4 -8
  8. data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
  9. data/examples/active_record_openid_store/lib/association.rb +2 -0
  10. data/examples/active_record_openid_store/lib/openid_ar_store.rb +22 -47
  11. data/examples/active_record_openid_store/test/store_test.rb +78 -48
  12. data/examples/discover +46 -0
  13. data/examples/{rails_server → rails_openid}/README +0 -0
  14. data/examples/{rails_server → rails_openid}/Rakefile +0 -0
  15. data/examples/{rails_server → rails_openid}/app/controllers/application.rb +0 -0
  16. data/examples/rails_openid/app/controllers/consumer_controller.rb +115 -0
  17. data/examples/{rails_server → rails_openid}/app/controllers/login_controller.rb +10 -2
  18. data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
  19. data/examples/{rails_server → rails_openid}/app/helpers/application_helper.rb +0 -0
  20. data/examples/{rails_server → rails_openid}/app/helpers/login_helper.rb +0 -0
  21. data/examples/{rails_server → rails_openid}/app/helpers/server_helper.rb +0 -0
  22. data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
  23. data/examples/rails_openid/app/views/consumer/start.rhtml +8 -0
  24. data/examples/{rails_server → rails_openid}/app/views/layouts/server.rhtml +0 -0
  25. data/examples/{rails_server → rails_openid}/app/views/login/index.rhtml +1 -1
  26. data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
  27. data/examples/{rails_server → rails_openid}/config/boot.rb +0 -0
  28. data/examples/{rails_server → rails_openid}/config/database.yml +0 -0
  29. data/examples/{rails_server → rails_openid}/config/environment.rb +0 -0
  30. data/examples/{rails_server → rails_openid}/config/environments/development.rb +0 -0
  31. data/examples/{rails_server → rails_openid}/config/environments/production.rb +0 -0
  32. data/examples/{rails_server → rails_openid}/config/environments/test.rb +0 -0
  33. data/examples/{rails_server → rails_openid}/config/routes.rb +2 -1
  34. data/examples/{rails_server → rails_openid}/doc/README_FOR_APP +0 -0
  35. data/examples/{rails_server → rails_openid}/public/404.html +0 -0
  36. data/examples/{rails_server → rails_openid}/public/500.html +0 -0
  37. data/examples/{rails_server → rails_openid}/public/dispatch.cgi +0 -0
  38. data/examples/{rails_server → rails_openid}/public/dispatch.fcgi +0 -0
  39. data/examples/{rails_server → rails_openid}/public/dispatch.rb +0 -0
  40. data/examples/{rails_server → rails_openid}/public/favicon.ico +0 -0
  41. data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
  42. data/examples/{rails_server → rails_openid}/public/javascripts/controls.js +0 -0
  43. data/examples/{rails_server → rails_openid}/public/javascripts/dragdrop.js +0 -0
  44. data/examples/{rails_server → rails_openid}/public/javascripts/effects.js +0 -0
  45. data/examples/{rails_server → rails_openid}/public/javascripts/prototype.js +0 -0
  46. data/examples/{rails_server → rails_openid}/public/robots.txt +0 -0
  47. data/examples/{rails_server → rails_openid}/script/about +0 -0
  48. data/examples/{rails_server → rails_openid}/script/breakpointer +0 -0
  49. data/examples/{rails_server → rails_openid}/script/console +0 -0
  50. data/examples/{rails_server → rails_openid}/script/destroy +0 -0
  51. data/examples/{rails_server → rails_openid}/script/generate +0 -0
  52. data/examples/{rails_server → rails_openid}/script/performance/benchmarker +0 -0
  53. data/examples/{rails_server → rails_openid}/script/performance/profiler +0 -0
  54. data/examples/{rails_server → rails_openid}/script/plugin +0 -0
  55. data/examples/{rails_server → rails_openid}/script/process/reaper +0 -0
  56. data/examples/{rails_server → rails_openid}/script/process/spawner +0 -0
  57. data/examples/{rails_server → rails_openid}/script/process/spinner +0 -0
  58. data/examples/{rails_server → rails_openid}/script/runner +0 -0
  59. data/examples/{rails_server → rails_openid}/script/server +0 -0
  60. data/examples/{rails_server → rails_openid}/test/functional/login_controller_test.rb +0 -0
  61. data/examples/{rails_server → rails_openid}/test/functional/server_controller_test.rb +0 -0
  62. data/examples/{rails_server → rails_openid}/test/test_helper.rb +0 -0
  63. data/lib/{hmac.rb → hmac/hmac.rb} +0 -0
  64. data/lib/{hmac-sha1.rb → hmac/sha1.rb} +1 -1
  65. data/lib/{hmac-sha2.rb → hmac/sha2.rb} +1 -1
  66. data/lib/openid/association.rb +213 -73
  67. data/lib/openid/consumer/associationmanager.rb +338 -0
  68. data/lib/openid/consumer/checkid_request.rb +175 -0
  69. data/lib/openid/consumer/discovery.rb +480 -0
  70. data/lib/openid/consumer/discovery_manager.rb +123 -0
  71. data/lib/openid/consumer/html_parse.rb +136 -0
  72. data/lib/openid/consumer/idres.rb +525 -0
  73. data/lib/openid/consumer/responses.rb +133 -0
  74. data/lib/openid/consumer.rb +280 -807
  75. data/lib/openid/cryptutil.rb +85 -0
  76. data/lib/openid/dh.rb +60 -23
  77. data/lib/openid/extension.rb +31 -0
  78. data/lib/openid/extensions/ax.rb +506 -0
  79. data/lib/openid/extensions/pape.rb +182 -0
  80. data/lib/openid/extensions/sreg.rb +275 -0
  81. data/lib/openid/extras.rb +11 -0
  82. data/lib/openid/fetchers.rb +132 -93
  83. data/lib/openid/kvform.rb +133 -0
  84. data/lib/openid/kvpost.rb +56 -0
  85. data/lib/openid/message.rb +534 -0
  86. data/lib/openid/protocolerror.rb +6 -0
  87. data/lib/openid/server.rb +1215 -666
  88. data/lib/openid/store/filesystem.rb +271 -0
  89. data/lib/openid/store/interface.rb +75 -0
  90. data/lib/openid/store/memory.rb +84 -0
  91. data/lib/openid/store/nonce.rb +68 -0
  92. data/lib/openid/trustroot.rb +314 -87
  93. data/lib/openid/urinorm.rb +37 -34
  94. data/lib/openid/util.rb +42 -220
  95. data/lib/openid/yadis/accept.rb +148 -0
  96. data/lib/openid/yadis/constants.rb +21 -0
  97. data/lib/openid/yadis/discovery.rb +153 -0
  98. data/lib/openid/yadis/filters.rb +205 -0
  99. data/lib/openid/{htmltokenizer.rb → yadis/htmltokenizer.rb} +1 -54
  100. data/lib/openid/yadis/parsehtml.rb +36 -0
  101. data/lib/openid/yadis/services.rb +42 -0
  102. data/lib/openid/yadis/xrds.rb +171 -0
  103. data/lib/openid/yadis/xri.rb +90 -0
  104. data/lib/openid/yadis/xrires.rb +106 -0
  105. data/lib/openid.rb +1 -4
  106. data/test/data/accept.txt +124 -0
  107. data/test/data/dh.txt +29 -0
  108. data/test/data/example-xrds.xml +14 -0
  109. data/test/data/linkparse.txt +587 -0
  110. data/test/data/n2b64 +650 -0
  111. data/test/data/test1-discover.txt +137 -0
  112. data/test/data/test1-parsehtml.txt +128 -0
  113. data/test/data/test_discover/openid.html +11 -0
  114. data/test/data/test_discover/openid2.html +11 -0
  115. data/test/data/test_discover/openid2_xrds.xml +12 -0
  116. data/test/data/test_discover/openid2_xrds_no_local_id.xml +11 -0
  117. data/test/data/test_discover/openid_1_and_2.html +11 -0
  118. data/test/data/test_discover/openid_1_and_2_xrds.xml +16 -0
  119. data/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
  120. data/test/data/test_discover/openid_and_yadis.html +12 -0
  121. data/test/data/test_discover/openid_no_delegate.html +10 -0
  122. data/test/data/test_discover/yadis_0entries.xml +12 -0
  123. data/test/data/test_discover/yadis_2_bad_local_id.xml +15 -0
  124. data/test/data/test_discover/yadis_2entries_delegate.xml +22 -0
  125. data/test/data/test_discover/yadis_2entries_idp.xml +21 -0
  126. data/test/data/test_discover/yadis_another_delegate.xml +14 -0
  127. data/test/data/test_discover/yadis_idp.xml +12 -0
  128. data/test/data/test_discover/yadis_idp_delegate.xml +13 -0
  129. data/test/data/test_discover/yadis_no_delegate.xml +11 -0
  130. data/test/data/test_xrds/=j3h.2007.11.14.xrds +25 -0
  131. data/test/data/test_xrds/README +12 -0
  132. data/test/data/test_xrds/delegated-20060809-r1.xrds +34 -0
  133. data/test/data/test_xrds/delegated-20060809-r2.xrds +34 -0
  134. data/test/data/test_xrds/delegated-20060809.xrds +34 -0
  135. data/test/data/test_xrds/no-xrd.xml +7 -0
  136. data/test/data/test_xrds/not-xrds.xml +2 -0
  137. data/test/data/test_xrds/prefixsometimes.xrds +34 -0
  138. data/test/data/test_xrds/ref.xrds +109 -0
  139. data/test/data/test_xrds/sometimesprefix.xrds +34 -0
  140. data/test/data/test_xrds/spoof1.xrds +25 -0
  141. data/test/data/test_xrds/spoof2.xrds +25 -0
  142. data/test/data/test_xrds/spoof3.xrds +37 -0
  143. data/test/data/test_xrds/status222.xrds +9 -0
  144. data/test/data/test_xrds/valid-populated-xrds.xml +39 -0
  145. data/test/data/trustroot.txt +147 -0
  146. data/test/discoverdata.rb +131 -0
  147. data/test/test_accept.rb +170 -0
  148. data/test/test_association.rb +266 -0
  149. data/test/test_associationmanager.rb +899 -0
  150. data/test/test_ax.rb +587 -0
  151. data/test/test_checkid_request.rb +297 -0
  152. data/test/test_consumer.rb +257 -0
  153. data/test/test_cryptutil.rb +117 -0
  154. data/test/test_dh.rb +86 -0
  155. data/test/test_discover.rb +772 -0
  156. data/test/test_discovery_manager.rb +262 -0
  157. data/test/test_extras.rb +35 -0
  158. data/test/test_fetchers.rb +472 -0
  159. data/test/test_filters.rb +270 -0
  160. data/test/test_idres.rb +816 -0
  161. data/test/test_kvform.rb +165 -0
  162. data/test/test_kvpost.rb +65 -0
  163. data/test/test_linkparse.rb +101 -0
  164. data/test/test_message.rb +1058 -0
  165. data/test/test_nonce.rb +89 -0
  166. data/test/test_openid_yadis.rb +178 -0
  167. data/test/test_pape.rb +233 -0
  168. data/test/test_parsehtml.rb +80 -0
  169. data/test/test_responses.rb +63 -0
  170. data/test/test_server.rb +2270 -0
  171. data/test/test_sreg.rb +479 -0
  172. data/test/test_stores.rb +269 -0
  173. data/test/test_trustroot.rb +112 -0
  174. data/test/{urinorm.rb → test_urinorm.rb} +6 -3
  175. data/test/test_util.rb +144 -0
  176. data/test/test_xrds.rb +160 -0
  177. data/test/test_xri.rb +48 -0
  178. data/test/test_xrires.rb +63 -0
  179. data/test/test_yadis_discovery.rb +207 -0
  180. data/test/testutil.rb +116 -0
  181. data/test/util.rb +47 -50
  182. metadata +233 -143
  183. data/examples/consumer.rb +0 -290
  184. data/examples/rails_openid_login_generator/openid_login_generator-0.1.gem +0 -0
  185. data/examples/rails_server/app/controllers/server_controller.rb +0 -190
  186. data/examples/rails_server/app/views/server/decide.rhtml +0 -11
  187. data/examples/rails_server/public/images/rails.png +0 -0
  188. data/lib/hmac-md5.rb +0 -11
  189. data/lib/hmac-rmd160.rb +0 -11
  190. data/lib/openid/discovery.rb +0 -122
  191. data/lib/openid/filestore.rb +0 -315
  192. data/lib/openid/parse.rb +0 -23
  193. data/lib/openid/service.rb +0 -147
  194. data/lib/openid/stores.rb +0 -178
  195. data/test/assoc.rb +0 -38
  196. data/test/consumer.rb +0 -376
  197. data/test/data/brian.xrds +0 -16
  198. data/test/data/brianellin.mylid.xrds +0 -42
  199. data/test/dh.rb +0 -20
  200. data/test/extensions.rb +0 -30
  201. data/test/linkparse.rb +0 -305
  202. data/test/runtests.rb +0 -22
  203. data/test/server2.rb +0 -1053
  204. data/test/service.rb +0 -47
  205. data/test/storetestcase.rb +0 -172
  206. data/test/teststore.rb +0 -47
  207. data/test/trustroot.rb +0 -117
@@ -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