ruby-openid2 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +136 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/CONTRIBUTING.md +54 -0
  6. data/LICENSE.txt +210 -0
  7. data/README.md +81 -0
  8. data/SECURITY.md +15 -0
  9. data/lib/hmac/hmac.rb +110 -0
  10. data/lib/hmac/sha1.rb +11 -0
  11. data/lib/hmac/sha2.rb +25 -0
  12. data/lib/openid/association.rb +246 -0
  13. data/lib/openid/consumer/associationmanager.rb +354 -0
  14. data/lib/openid/consumer/checkid_request.rb +179 -0
  15. data/lib/openid/consumer/discovery.rb +516 -0
  16. data/lib/openid/consumer/discovery_manager.rb +144 -0
  17. data/lib/openid/consumer/html_parse.rb +142 -0
  18. data/lib/openid/consumer/idres.rb +513 -0
  19. data/lib/openid/consumer/responses.rb +147 -0
  20. data/lib/openid/consumer/session.rb +36 -0
  21. data/lib/openid/consumer.rb +406 -0
  22. data/lib/openid/cryptutil.rb +112 -0
  23. data/lib/openid/dh.rb +84 -0
  24. data/lib/openid/extension.rb +38 -0
  25. data/lib/openid/extensions/ax.rb +552 -0
  26. data/lib/openid/extensions/oauth.rb +88 -0
  27. data/lib/openid/extensions/pape.rb +170 -0
  28. data/lib/openid/extensions/sreg.rb +268 -0
  29. data/lib/openid/extensions/ui.rb +49 -0
  30. data/lib/openid/fetchers.rb +277 -0
  31. data/lib/openid/kvform.rb +113 -0
  32. data/lib/openid/kvpost.rb +62 -0
  33. data/lib/openid/message.rb +555 -0
  34. data/lib/openid/protocolerror.rb +7 -0
  35. data/lib/openid/server.rb +1571 -0
  36. data/lib/openid/store/filesystem.rb +260 -0
  37. data/lib/openid/store/interface.rb +73 -0
  38. data/lib/openid/store/memcache.rb +109 -0
  39. data/lib/openid/store/memory.rb +79 -0
  40. data/lib/openid/store/nonce.rb +72 -0
  41. data/lib/openid/trustroot.rb +597 -0
  42. data/lib/openid/urinorm.rb +72 -0
  43. data/lib/openid/util.rb +119 -0
  44. data/lib/openid/version.rb +5 -0
  45. data/lib/openid/yadis/accept.rb +141 -0
  46. data/lib/openid/yadis/constants.rb +16 -0
  47. data/lib/openid/yadis/discovery.rb +151 -0
  48. data/lib/openid/yadis/filters.rb +192 -0
  49. data/lib/openid/yadis/htmltokenizer.rb +290 -0
  50. data/lib/openid/yadis/parsehtml.rb +50 -0
  51. data/lib/openid/yadis/services.rb +44 -0
  52. data/lib/openid/yadis/xrds.rb +160 -0
  53. data/lib/openid/yadis/xri.rb +86 -0
  54. data/lib/openid/yadis/xrires.rb +87 -0
  55. data/lib/openid.rb +27 -0
  56. data/lib/ruby-openid.rb +1 -0
  57. data.tar.gz.sig +0 -0
  58. metadata +331 -0
  59. metadata.gz.sig +0 -0
@@ -0,0 +1,555 @@
1
+ require_relative "util"
2
+ require_relative "kvform"
3
+
4
+ module OpenID
5
+ IDENTIFIER_SELECT = "http://specs.openid.net/auth/2.0/identifier_select"
6
+
7
+ # URI for Simple Registration extension, the only commonly deployed
8
+ # OpenID 1.x extension, and so a special case.
9
+ SREG_URI = "http://openid.net/sreg/1.0"
10
+
11
+ # The OpenID 1.x namespace URIs
12
+ OPENID1_NS = "http://openid.net/signon/1.0"
13
+ OPENID11_NS = "http://openid.net/signon/1.1"
14
+ OPENID1_NAMESPACES = [OPENID1_NS, OPENID11_NS]
15
+
16
+ # The OpenID 2.0 namespace URI
17
+ OPENID2_NS = "http://specs.openid.net/auth/2.0"
18
+
19
+ # The namespace consisting of pairs with keys that are prefixed with
20
+ # "openid." but not in another namespace.
21
+ NULL_NAMESPACE = :null_namespace
22
+
23
+ # The null namespace, when it is an allowed OpenID namespace
24
+ OPENID_NS = :openid_namespace
25
+
26
+ # The top-level namespace, excluding all pairs with keys that start
27
+ # with "openid."
28
+ BARE_NS = :bare_namespace
29
+
30
+ # Limit, in bytes, of identity provider and return_to URLs,
31
+ # including response payload. See OpenID 1.1 specification,
32
+ # Appendix D.
33
+ OPENID1_URL_LIMIT = 2047
34
+
35
+ # All OpenID protocol fields. Used to check namespace aliases.
36
+ OPENID_PROTOCOL_FIELDS = %w[
37
+ ns
38
+ mode
39
+ error
40
+ return_to
41
+ contact
42
+ reference
43
+ signed
44
+ assoc_type
45
+ session_type
46
+ dh_modulus
47
+ dh_gen
48
+ dh_consumer_public
49
+ claimed_id
50
+ identity
51
+ realm
52
+ invalidate_handle
53
+ op_endpoint
54
+ response_nonce
55
+ sig
56
+ assoc_handle
57
+ trust_root
58
+ openid
59
+ ]
60
+
61
+ # Sentinel used for Message implementation to indicate that getArg
62
+ # should raise an exception instead of returning a default.
63
+ NO_DEFAULT = :no_default
64
+
65
+ # Raised if the generic OpenID namespace is accessed when there
66
+ # is no OpenID namespace set for this message.
67
+ class UndefinedOpenIDNamespace < Exception; end
68
+
69
+ # Raised when an alias or namespace URI has already been registered.
70
+ class NamespaceAliasRegistrationError < Exception; end
71
+
72
+ # Raised if openid.ns is not a recognized value.
73
+ # See Message class variable @@allowed_openid_namespaces
74
+ class InvalidOpenIDNamespace < Exception; end
75
+
76
+ class Message
77
+ attr_reader :namespaces
78
+
79
+ # Raised when key lookup fails
80
+ class KeyNotFound < IndexError; end
81
+
82
+ # Namespace / alias registration map. See
83
+ # register_namespace_alias.
84
+ @@registered_aliases = {}
85
+
86
+ # Registers a (namespace URI, alias) mapping in a global namespace
87
+ # alias map. Raises NamespaceAliasRegistrationError if either the
88
+ # namespace URI or alias has already been registered with a
89
+ # different value. This function is required if you want to use a
90
+ # namespace with an OpenID 1 message.
91
+ def self.register_namespace_alias(namespace_uri, alias_)
92
+ return if @@registered_aliases[alias_] == namespace_uri
93
+
94
+ if @@registered_aliases.values.include?(namespace_uri)
95
+ raise NamespaceAliasRegistrationError,
96
+ 'Namespace uri #{namespace_uri} already registered'
97
+ end
98
+
99
+ if @@registered_aliases.member?(alias_)
100
+ raise NamespaceAliasRegistrationError,
101
+ 'Alias #{alias_} already registered'
102
+ end
103
+
104
+ @@registered_aliases[alias_] = namespace_uri
105
+ end
106
+
107
+ @@allowed_openid_namespaces = [OPENID1_NS, OPENID2_NS, OPENID11_NS]
108
+
109
+ # Raises InvalidNamespaceError if you try to instantiate a Message
110
+ # with a namespace not in the above allowed list
111
+ def initialize(openid_namespace = nil)
112
+ @args = {}
113
+ @namespaces = NamespaceMap.new
114
+ if openid_namespace
115
+ implicit = OPENID1_NAMESPACES.member?(openid_namespace)
116
+ set_openid_namespace(openid_namespace, implicit)
117
+ else
118
+ @openid_ns_uri = nil
119
+ end
120
+ end
121
+
122
+ # Construct a Message containing a set of POST arguments.
123
+ # Raises InvalidNamespaceError if you try to instantiate a Message
124
+ # with a namespace not in the above allowed list
125
+ def self.from_post_args(args)
126
+ m = Message.new
127
+ openid_args = {}
128
+ args.each do |key, value|
129
+ if value.is_a?(Array)
130
+ raise ArgumentError, "Query dict must have one value for each key, " +
131
+ "not lists of values. Query is #{args.inspect}"
132
+ end
133
+
134
+ prefix, rest = key.split(".", 2)
135
+
136
+ if prefix != "openid" or rest.nil?
137
+ m.set_arg(BARE_NS, key, value)
138
+ else
139
+ openid_args[rest] = value
140
+ end
141
+ end
142
+
143
+ m._from_openid_args(openid_args)
144
+ m
145
+ end
146
+
147
+ # Construct a Message from a parsed KVForm message.
148
+ # Raises InvalidNamespaceError if you try to instantiate a Message
149
+ # with a namespace not in the above allowed list
150
+ def self.from_openid_args(openid_args)
151
+ m = Message.new
152
+ m._from_openid_args(openid_args)
153
+ m
154
+ end
155
+
156
+ # Raises InvalidNamespaceError if you try to instantiate a Message
157
+ # with a namespace not in the above allowed list
158
+ def _from_openid_args(openid_args)
159
+ ns_args = []
160
+
161
+ # resolve namespaces
162
+ openid_args.each do |rest, value|
163
+ ns_alias, ns_key = rest.split(".", 2)
164
+ if ns_key.nil?
165
+ ns_alias = NULL_NAMESPACE
166
+ ns_key = rest
167
+ end
168
+
169
+ if ns_alias == "ns"
170
+ @namespaces.add_alias(value, ns_key)
171
+ elsif ns_alias == NULL_NAMESPACE and ns_key == "ns"
172
+ set_openid_namespace(value, false)
173
+ else
174
+ ns_args << [ns_alias, ns_key, value]
175
+ end
176
+ end
177
+
178
+ # implicitly set an OpenID 1 namespace
179
+ set_openid_namespace(OPENID1_NS, true) unless get_openid_namespace
180
+
181
+ # put the pairs into the appropriate namespaces
182
+ ns_args.each do |ns_alias, ns_key, value|
183
+ ns_uri = @namespaces.get_namespace_uri(ns_alias)
184
+ unless ns_uri
185
+ ns_uri = _get_default_namespace(ns_alias)
186
+ if ns_uri
187
+ @namespaces.add_alias(ns_uri, ns_alias, true)
188
+ else
189
+ ns_uri = get_openid_namespace
190
+ ns_key = "#{ns_alias}.#{ns_key}"
191
+ end
192
+ end
193
+ set_arg(ns_uri, ns_key, value)
194
+ end
195
+ end
196
+
197
+ def _get_default_namespace(mystery_alias)
198
+ # only try to map an alias to a default if it's an
199
+ # OpenID 1.x namespace
200
+ @@registered_aliases[mystery_alias] if is_openid1
201
+ end
202
+
203
+ def set_openid_namespace(openid_ns_uri, implicit)
204
+ unless @@allowed_openid_namespaces.include?(openid_ns_uri)
205
+ raise InvalidOpenIDNamespace, "Invalid null namespace: #{openid_ns_uri}"
206
+ end
207
+
208
+ @namespaces.add_alias(openid_ns_uri, NULL_NAMESPACE, implicit)
209
+ @openid_ns_uri = openid_ns_uri
210
+ end
211
+
212
+ def get_openid_namespace
213
+ @openid_ns_uri
214
+ end
215
+
216
+ def is_openid1
217
+ OPENID1_NAMESPACES.member?(@openid_ns_uri)
218
+ end
219
+
220
+ def is_openid2
221
+ @openid_ns_uri == OPENID2_NS
222
+ end
223
+
224
+ # Create a message from a KVForm string
225
+ def self.from_kvform(kvform_string)
226
+ Message.from_openid_args(Util.kv_to_dict(kvform_string))
227
+ end
228
+
229
+ def copy
230
+ Marshal.load(Marshal.dump(self))
231
+ end
232
+
233
+ # Return all arguments with "openid." in from of namespaced arguments.
234
+ def to_post_args
235
+ args = {}
236
+
237
+ # add namespace defs to the output
238
+ @namespaces.each do |ns_uri, ns_alias|
239
+ next if @namespaces.implicit?(ns_uri)
240
+
241
+ ns_key = if ns_alias == NULL_NAMESPACE
242
+ "openid.ns"
243
+ else
244
+ "openid.ns." + ns_alias
245
+ end
246
+ args[ns_key] = ns_uri
247
+ end
248
+
249
+ @args.each do |k, value|
250
+ ns_uri, ns_key = k
251
+ key = get_key(ns_uri, ns_key)
252
+ args[key] = value
253
+ end
254
+
255
+ args
256
+ end
257
+
258
+ # Return all namespaced arguments, failing if any non-namespaced arguments
259
+ # exist.
260
+ def to_args
261
+ post_args = to_post_args
262
+ kvargs = {}
263
+ post_args.each do |k, v|
264
+ if !k.start_with?("openid.")
265
+ raise ArgumentError,
266
+ "This message can only be encoded as a POST, because it contains arguments that are not prefixed with 'openid.'"
267
+ else
268
+ kvargs[k[7..-1]] = v
269
+ end
270
+ end
271
+ kvargs
272
+ end
273
+
274
+ # Generate HTML form markup that contains the values in this
275
+ # message, to be HTTP POSTed as x-www-form-urlencoded UTF-8.
276
+ def to_form_markup(action_url, form_tag_attrs = nil, submit_text = "Continue")
277
+ form_tag_attr_map = {}
278
+
279
+ if form_tag_attrs
280
+ form_tag_attrs.each do |name, attr|
281
+ form_tag_attr_map[name] = attr
282
+ end
283
+ end
284
+
285
+ form_tag_attr_map["action"] = action_url
286
+ form_tag_attr_map["method"] = "post"
287
+ form_tag_attr_map["accept-charset"] = "UTF-8"
288
+ form_tag_attr_map["enctype"] = "application/x-www-form-urlencoded"
289
+
290
+ markup = "<form "
291
+
292
+ form_tag_attr_map.each do |k, v|
293
+ markup += " #{k}=\"#{v}\""
294
+ end
295
+
296
+ markup += ">\n"
297
+
298
+ to_post_args.each do |k, v|
299
+ markup += "<input type='hidden' name='#{k}' value='#{OpenID::Util.html_encode(v)}' />\n"
300
+ end
301
+ markup += "<input type='submit' value='#{submit_text}' />\n"
302
+ markup += "\n</form>"
303
+ markup
304
+ end
305
+
306
+ # Generate a GET URL with the paramters in this message attacked as
307
+ # query parameters.
308
+ def to_url(base_url)
309
+ Util.append_args(base_url, to_post_args)
310
+ end
311
+
312
+ # Generate a KVForm string that contains the parameters in this message.
313
+ # This will fail is the message contains arguments outside of the
314
+ # "openid." prefix.
315
+ def to_kvform
316
+ Util.dict_to_kv(to_args)
317
+ end
318
+
319
+ # Generate an x-www-urlencoded string.
320
+ def to_url_encoded
321
+ args = to_post_args.map.sort
322
+ Util.urlencode(args)
323
+ end
324
+
325
+ # Convert an input value into the internally used values of this obejct.
326
+ def _fix_ns(namespace)
327
+ if namespace == OPENID_NS
328
+ raise UndefinedOpenIDNamespace, "OpenID namespace not set" unless @openid_ns_uri
329
+
330
+ namespace = @openid_ns_uri
331
+
332
+ end
333
+
334
+ return namespace if namespace == BARE_NS
335
+
336
+ unless namespace.is_a?(String)
337
+ raise ArgumentError, "Namespace must be BARE_NS, OPENID_NS or " \
338
+ "a string. Got #{namespace.inspect}"
339
+ end
340
+
341
+ if namespace.index(":").nil?
342
+ msg = "OpenID 2.0 namespace identifiers SHOULD be URIs. " \
343
+ "Got #{namespace.inspect}"
344
+ Util.log(msg)
345
+
346
+ if namespace == "sreg"
347
+ msg = "Using #{SREG_URI} instead of \"sreg\" as namespace"
348
+ Util.log(msg)
349
+ return SREG_URI
350
+ end
351
+ end
352
+
353
+ namespace
354
+ end
355
+
356
+ def has_key?(namespace, ns_key)
357
+ namespace = _fix_ns(namespace)
358
+ @args.member?([namespace, ns_key])
359
+ end
360
+
361
+ # Get the key for a particular namespaced argument
362
+ def get_key(namespace, ns_key)
363
+ namespace = _fix_ns(namespace)
364
+ return ns_key if namespace == BARE_NS
365
+
366
+ ns_alias = @namespaces.get_alias(namespace)
367
+
368
+ # no alias is defined, so no key can exist
369
+ return if ns_alias.nil?
370
+
371
+ tail = if ns_alias == NULL_NAMESPACE
372
+ ns_key
373
+ else
374
+ "#{ns_alias}.#{ns_key}"
375
+ end
376
+
377
+ "openid." + tail
378
+ end
379
+
380
+ # Get a value for a namespaced key.
381
+ def get_arg(namespace, key, default = nil)
382
+ namespace = _fix_ns(namespace)
383
+ @args.fetch([namespace, key]) do
384
+ raise KeyNotFound, "<#{namespace}>#{key} not in this message" if default == NO_DEFAULT
385
+
386
+ default
387
+ end
388
+ end
389
+
390
+ # Get the arguments that are defined for this namespace URI.
391
+ def get_args(namespace)
392
+ namespace = _fix_ns(namespace)
393
+ args = {}
394
+ @args.each do |k, v|
395
+ pair_ns, ns_key = k
396
+ args[ns_key] = v if pair_ns == namespace
397
+ end
398
+ args
399
+ end
400
+
401
+ # Set multiple key/value pairs in one call.
402
+ def update_args(namespace, updates)
403
+ namespace = _fix_ns(namespace)
404
+ updates.each { |k, v| set_arg(namespace, k, v) }
405
+ end
406
+
407
+ # Set a single argument in this namespace
408
+ def set_arg(namespace, key, value)
409
+ namespace = _fix_ns(namespace)
410
+ @args[[namespace, key].freeze] = value
411
+ @namespaces.add(namespace) if namespace != BARE_NS
412
+ end
413
+
414
+ # Remove a single argument from this namespace.
415
+ def del_arg(namespace, key)
416
+ namespace = _fix_ns(namespace)
417
+ _key = [namespace, key]
418
+ @args.delete(_key)
419
+ end
420
+
421
+ def ==(other)
422
+ other.is_a?(self.class) && @args == other.instance_eval { @args }
423
+ end
424
+
425
+ def get_aliased_arg(aliased_key, default = nil)
426
+ return get_openid_namespace if aliased_key == "ns"
427
+
428
+ ns_alias, key = aliased_key.split(".", 2)
429
+ if ns_alias == "ns"
430
+ uri = @namespaces.get_namespace_uri(key)
431
+ return (uri.nil? ? default : uri) unless uri.nil? and default == NO_DEFAULT
432
+
433
+ raise KeyNotFound, "Namespace #{key} not defined when looking " \
434
+ "for #{aliased_key}"
435
+
436
+ end
437
+
438
+ if key.nil?
439
+ key = aliased_key
440
+ ns = nil
441
+ else
442
+ ns = @namespaces.get_namespace_uri(ns_alias)
443
+ end
444
+
445
+ if ns.nil?
446
+ key = aliased_key
447
+ ns = get_openid_namespace
448
+ end
449
+
450
+ get_arg(ns, key, default)
451
+ end
452
+ end
453
+
454
+ # Maintains a bidirectional map between namespace URIs and aliases.
455
+ class NamespaceMap
456
+ def initialize
457
+ @alias_to_namespace = {}
458
+ @namespace_to_alias = {}
459
+ @implicit_namespaces = []
460
+ end
461
+
462
+ def get_alias(namespace_uri)
463
+ @namespace_to_alias[namespace_uri]
464
+ end
465
+
466
+ def get_namespace_uri(namespace_alias)
467
+ @alias_to_namespace[namespace_alias]
468
+ end
469
+
470
+ # Add an alias from this namespace URI to the alias.
471
+ def add_alias(namespace_uri, desired_alias, implicit = false)
472
+ # Check that desired_alias is not an openid protocol field as
473
+ # per the spec.
474
+ Util.truthy_assert(
475
+ !OPENID_PROTOCOL_FIELDS.include?(desired_alias),
476
+ "#{desired_alias} is not an allowed namespace alias",
477
+ )
478
+
479
+ # check that there is not a namespace already defined for the
480
+ # desired alias
481
+ current_namespace_uri = @alias_to_namespace.fetch(desired_alias, nil)
482
+ if current_namespace_uri and current_namespace_uri != namespace_uri
483
+ raise IndexError,
484
+ "Cannot map #{namespace_uri} to alias #{desired_alias}. #{current_namespace_uri} is already mapped to alias #{desired_alias}"
485
+ end
486
+
487
+ # Check that desired_alias does not contain a period as per the
488
+ # spec.
489
+ if desired_alias.is_a?(String)
490
+ Util.truthy_assert(
491
+ desired_alias.index(".").nil?,
492
+ "#{desired_alias} must not contain a dot",
493
+ )
494
+ end
495
+
496
+ # check that there is not already a (different) alias for this
497
+ # namespace URI.
498
+ _alias = @namespace_to_alias[namespace_uri]
499
+ if _alias and _alias != desired_alias
500
+ raise IndexError,
501
+ "Cannot map #{namespace_uri} to alias #{desired_alias}. It is already mapped to alias #{_alias}"
502
+ end
503
+
504
+ @alias_to_namespace[desired_alias] = namespace_uri
505
+ @namespace_to_alias[namespace_uri] = desired_alias
506
+ @implicit_namespaces << namespace_uri if implicit
507
+ desired_alias
508
+ end
509
+
510
+ # Add this namespace URI to the mapping, without caring what alias
511
+ # it ends up with.
512
+ def add(namespace_uri)
513
+ # see if this namepace is already mapped to an alias
514
+ _alias = @namespace_to_alias[namespace_uri]
515
+ return _alias if _alias
516
+
517
+ # Fall back to generating a numberical alias
518
+ i = 0
519
+ while true
520
+ _alias = "ext" + i.to_s
521
+ begin
522
+ add_alias(namespace_uri, _alias)
523
+ rescue IndexError
524
+ i += 1
525
+ else
526
+ return _alias
527
+ end
528
+ end
529
+
530
+ raise StandardError, "Unreachable"
531
+ end
532
+
533
+ def member?(namespace_uri)
534
+ @namespace_to_alias.has_key?(namespace_uri)
535
+ end
536
+
537
+ def each(&block)
538
+ @namespace_to_alias.each(&block)
539
+ end
540
+
541
+ def namespace_uris
542
+ # Return an iterator over the namespace URIs
543
+ @namespace_to_alias.keys
544
+ end
545
+
546
+ def implicit?(namespace_uri)
547
+ @implicit_namespaces.member?(namespace_uri)
548
+ end
549
+
550
+ def aliases
551
+ # Return an iterator over the aliases
552
+ @alias_to_namespace.keys
553
+ end
554
+ end
555
+ end
@@ -0,0 +1,7 @@
1
+ require_relative "util"
2
+
3
+ module OpenID
4
+ # An error in the OpenID protocol
5
+ class ProtocolError < OpenIDError
6
+ end
7
+ end