C0 code coverage information

Generated on Fri Jul 11 15:55:34 -0700 2008 with rcov 0.7.0


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

Generated using the rcov code coverage analysis tool for Ruby version 0.7.0.

Valid XHTML 1.0! Valid CSS!