C0 code coverage information

Generated on Fri Jul 11 15:55:31 -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/consumer.rb 395 134
98.2% 
94.8% 
  1 require "openid/consumer/idres.rb"
  2 require "openid/consumer/checkid_request.rb"
  3 require "openid/consumer/associationmanager.rb"
  4 require "openid/consumer/responses.rb"
  5 require "openid/consumer/discovery_manager"
  6 require "openid/consumer/discovery"
  7 require "openid/message"
  8 require "openid/yadis/discovery"
  9 require "openid/store/nonce"
 10 
 11 module OpenID
 12   # OpenID support for Relying Parties (aka Consumers).
 13   #
 14   # This module documents the main interface with the OpenID consumer
 15   # library.  The only part of the library which has to be used and
 16   # isn't documented in full here is the store required to create an
 17   # Consumer instance.
 18   #
 19   # = OVERVIEW
 20   #
 21   # The OpenID identity verification process most commonly uses the
 22   # following steps, as visible to the user of this library:
 23   #
 24   # 1. The user enters their OpenID into a field on the consumer's
 25   #    site, and hits a login button.
 26   #
 27   # 2. The consumer site discovers the user's OpenID provider using
 28   #    the Yadis protocol.
 29   #
 30   # 3. The consumer site sends the browser a redirect to the OpenID
 31   #    provider.  This is the authentication request as described in
 32   #    the OpenID specification.
 33   #
 34   # 4. The OpenID provider's site sends the browser a redirect back to
 35   #    the consumer site.  This redirect contains the provider's
 36   #    response to the authentication request.
 37   #
 38   # The most important part of the flow to note is the consumer's site
 39   # must handle two separate HTTP requests in order to perform the
 40   # full identity check.
 41   #
 42   # = LIBRARY DESIGN
 43   #
 44   # This consumer library is designed with that flow in mind.  The
 45   # goal is to make it as easy as possible to perform the above steps
 46   # securely.
 47   #
 48   # At a high level, there are two important parts in the consumer
 49   # library.  The first important part is this module, which contains
 50   # the interface to actually use this library.  The second is
 51   # openid/store/interface.rb, which describes the interface to use if
 52   # you need to create a custom method for storing the state this
 53   # library needs to maintain between requests.
 54   #
 55   # In general, the second part is less important for users of the
 56   # library to know about, as several implementations are provided
 57   # which cover a wide variety of situations in which consumers may
 58   # use the library.
 59   #
 60   # The Consumer class has methods corresponding to the actions
 61   # necessary in each of steps 2, 3, and 4 described in the overview.
 62   # Use of this library should be as easy as creating an Consumer
 63   # instance and calling the methods appropriate for the action the
 64   # site wants to take.
 65   #
 66   # This library automatically detects which version of the OpenID
 67   # protocol should be used for a transaction and constructs the
 68   # proper requests and responses.  Users of this library do not need
 69   # to worry about supporting multiple protocol versions; the library
 70   # supports them implicitly.  Depending on the version of the
 71   # protocol in use, the OpenID transaction may be more secure.  See
 72   # the OpenID specifications for more information.
 73   #
 74   # = SESSIONS, STORES, AND STATELESS MODE
 75   #
 76   # The Consumer object keeps track of two types of state:
 77   #
 78   # 1. State of the user's current authentication attempt.  Things
 79   #    like the identity URL, the list of endpoints discovered for
 80   #    that URL, and in case where some endpoints are unreachable, the
 81   #    list of endpoints already tried.  This state needs to be held
 82   #    from Consumer.begin() to Consumer.complete(), but it is only
 83   #    applicable to a single session with a single user agent, and at
 84   #    the end of the authentication process (i.e. when an OP replies
 85   #    with either <tt>id_res</tt>. or <tt>cancel</tt> it may be
 86   #    discarded.
 87   #
 88   # 2. State of relationships with servers, i.e. shared secrets
 89   #    (associations) with servers and nonces seen on signed messages.
 90   #    This information should persist from one session to the next
 91   #    and should not be bound to a particular user-agent.
 92   #
 93   # These two types of storage are reflected in the first two
 94   # arguments of Consumer's constructor, <tt>session</tt> and
 95   # <tt>store</tt>.  <tt>session</tt> is a dict-like object and we
 96   # hope your web framework provides you with one of these bound to
 97   # the user agent.  <tt>store</tt> is an instance of Store.
 98   #
 99   # Since the store does hold secrets shared between your application
100   # and the OpenID provider, you should be careful about how you use
101   # it in a shared hosting environment.  If the filesystem or database
102   # permissions of your web host allow strangers to read from them, do
103   # not store your data there!  If you have no safe place to store
104   # your data, construct your consumer with nil for the store, and it
105   # will operate only in stateless mode.  Stateless mode may be
106   # slower, put more load on the OpenID provider, and trusts the
107   # provider to keep you safe from replay attacks.
108   #
109   # Several store implementation are provided, and the interface is
110   # fully documented so that custom stores can be used as well.  See
111   # the documentation for the Consumer class for more information on
112   # the interface for stores.  The implementations that are provided
113   # allow the consumer site to store the necessary data in several
114   # different ways, including several SQL databases and normal files
115   # on disk.
116   #
117   # = IMMEDIATE MODE
118   #
119   # In the flow described above, the user may need to confirm to the
120   # OpenID provider that it's ok to disclose his or her identity.  The
121   # provider may draw pages asking for information from the user
122   # before it redirects the browser back to the consumer's site.  This
123   # is generally transparent to the consumer site, so it is typically
124   # ignored as an implementation detail.
125   #
126   # There can be times, however, where the consumer site wants to get
127   # a response immediately.  When this is the case, the consumer can
128   # put the library in immediate mode.  In immediate mode, there is an
129   # extra response possible from the server, which is essentially the
130   # server reporting that it doesn't have enough information to answer
131   # the question yet.
132   #
133   # = USING THIS LIBRARY
134   #
135   # Integrating this library into an application is usually a
136   # relatively straightforward process.  The process should basically
137   # follow this plan:
138   #
139   # Add an OpenID login field somewhere on your site.  When an OpenID
140   # is entered in that field and the form is submitted, it should make
141   # a request to the your site which includes that OpenID URL.
142   #
143   # First, the application should instantiate a Consumer with a
144   # session for per-user state and store for shared state using the
145   # store of choice.
146   #
147   # Next, the application should call the <tt>begin</tt> method of
148   # Consumer instance.  This method takes the OpenID URL as entered by
149   # the user.  The <tt>begin</tt> method returns a CheckIDRequest
150   # object.
151   #
152   # Next, the application should call the redirect_url method on the
153   # CheckIDRequest object.  The parameter <tt>return_to</tt> is the
154   # URL that the OpenID server will send the user back to after
155   # attempting to verify his or her identity.  The <tt>realm</tt>
156   # parameter is the URL (or URL pattern) that identifies your web
157   # site to the user when he or she is authorizing it.  Send a
158   # redirect to the resulting URL to the user's browser.
159   #
160   # That's the first half of the authentication process.  The second
161   # half of the process is done after the user's OpenID Provider sends
162   # the user's browser a redirect back to your site to complete their
163   # login.
164   #
165   # When that happens, the user will contact your site at the URL
166   # given as the <tt>return_to</tt> URL to the redirect_url call made
167   # above.  The request will have several query parameters added to
168   # the URL by the OpenID provider as the information necessary to
169   # finish the request.
170   #
171   # Get a Consumer instance with the same session and store as before
172   # and call its complete() method, passing in all the received query
173   # arguments and URL currently being handled.
174   #
175   # There are multiple possible return types possible from that
176   # method. These indicate the whether or not the login was
177   # successful, and include any additional information appropriate for
178   # their type.
179   class Consumer
180     attr_accessor :session_key_prefix
181 
182     # Initialize a Consumer instance.
183     #
184     # You should create a new instance of the Consumer object with
185     # every HTTP request that handles OpenID transactions.
186     #
187     # session: the session object to use to store request information.
188     # The session should behave like a hash.
189     #
190     # store: an object that implements the interface in Store.
191     def initialize(session, store)
192       @session = session
193       @store = store
194       @session_key_prefix = 'OpenID::Consumer::'
195     end
196 
197     # Start the OpenID authentication process. See steps 1-2 in the
198     # overview for the Consumer class.
199     #
200     # user_url: Identity URL given by the user. This method performs a
201     # textual transformation of the URL to try and make sure it is
202     # normalized. For example, a user_url of example.com will be
203     # normalized to http://example.com/ normalizing and resolving any
204     # redirects the server might issue.
205     #
206     # anonymous: A boolean value.  Whether to make an anonymous
207     # request of the OpenID provider.  Such a request does not ask for
208     # an authorization assertion for an OpenID identifier, but may be
209     # used with extensions to pass other data.  e.g. "I don't care who
210     # you are, but I'd like to know your time zone."
211     #
212     # Returns a CheckIDRequest object containing the discovered
213     # information, with a method for building a redirect URL to the
214     # server, as described in step 3 of the overview. This object may
215     # also be used to add extension arguments to the request, using
216     # its add_extension_arg method.
217     #
218     # Raises DiscoveryFailure when no OpenID server can be found for
219     # this URL.
220     def begin(openid_identifier, anonymous=false)
221       manager = discovery_manager(openid_identifier)
222       service = manager.get_next_service(&method(:discover))
223 
224       if service.nil?
225         raise DiscoveryFailure.new("No usable OpenID services were found "\
226                                    "for #{openid_identifier.inspect}", nil)
227       else
228         begin_without_discovery(service, anonymous)
229       end
230     end
231 
232     # Start OpenID verification without doing OpenID server
233     # discovery. This method is used internally by Consumer.begin()
234     # after discovery is performed, and exists to provide an interface
235     # for library users needing to perform their own discovery.
236     #
237     # service: an OpenID service endpoint descriptor.  This object and
238     # factories for it are found in the openid/consumer/discovery.rb
239     # module.
240     #
241     # Returns an OpenID authentication request object.
242     def begin_without_discovery(service, anonymous)
243       assoc = association_manager(service).get_association
244       checkid_request = CheckIDRequest.new(assoc, service)
245       checkid_request.anonymous = anonymous
246 
247       if service.compatibility_mode
248         rt_args = checkid_request.return_to_args
249         rt_args[Consumer.openid1_return_to_nonce_name] = Nonce.mk_nonce
250         rt_args[Consumer.openid1_return_to_claimed_id_name] =
251           service.claimed_id
252       end
253 
254       self.last_requested_endpoint = service
255       return checkid_request
256     end
257 
258     # Called to interpret the server's response to an OpenID
259     # request. It is called in step 4 of the flow described in the
260     # Consumer overview.
261     #
262     # query: A hash of the query parameters for this HTTP request.
263     # Note that in rails, this is <b>not</b> <tt>params</tt> but
264     # <tt>params.reject{|k,v|request.path_parameters[k]}</tt>
265     # because <tt>controller</tt> and <tt>action</tt> and other
266     # "path parameters" are included in params.
267     #
268     # current_url: Extract the URL of the current request from your
269     # application's web request framework and specify it here to have it
270     # checked against the openid.return_to value in the response.  Do not
271     # just pass <tt>args['openid.return_to']</tt> here; that will defeat the
272     # purpose of this check.  (See OpenID Authentication 2.0 section 11.1.)
273     #
274     # If the return_to URL check fails, the status of the completion will be
275     # FAILURE.
276 
277     #
278     # Returns a subclass of Response. The type of response is
279     # indicated by the status attribute, which will be one of
280     # SUCCESS, CANCEL, FAILURE, or SETUP_NEEDED.
281     def complete(query, current_url)
282       message = Message.from_post_args(query)
283       mode = message.get_arg(OPENID_NS, 'mode', 'invalid')
284       begin
285         meth = method('complete_' + mode)
286       rescue NameError
287         meth = method(:complete_invalid)
288       end
289       response = meth.call(message, current_url)
290       cleanup_last_requested_endpoint
291       if [SUCCESS, CANCEL].member?(response.status)
292         cleanup_session
293       end
294       return response
295     end
296 
297     protected
298 
299     def session_get(name)
300       @session[session_key(name)]
301     end
302 
303     def session_set(name, val)
304       @session[session_key(name)] = val
305     end
306 
307     def session_key(suffix)
308       @session_key_prefix + suffix
309     end
310 
311     def last_requested_endpoint
312       session_get('last_requested_endpoint')
313     end
314 
315     def last_requested_endpoint=(endpoint)
316       session_set('last_requested_endpoint', endpoint)
317     end
318 
319     def cleanup_last_requested_endpoint
320       @session[session_key('last_requested_endpoint')] = nil
321     end
322 
323     def discovery_manager(openid_identifier)
324       DiscoveryManager.new(@session, openid_identifier, @session_key_prefix)
325     end
326 
327     def cleanup_session
328       discovery_manager(nil).cleanup(true)
329     end
330 
331 
332     def discover(identifier)
333       OpenID.discover(identifier)
334     end
335 
336     def negotiator
337       DefaultNegotiator
338     end
339 
340     def association_manager(service)
341       AssociationManager.new(@store, service.server_url,
342                              service.compatibility_mode, negotiator)
343     end
344 
345     def handle_idres(message, current_url)
346       IdResHandler.new(message, current_url, @store, last_requested_endpoint)
347     end
348 
349     def complete_invalid(message, unused_return_to)
350       mode = message.get_arg(OPENID_NS, 'mode', '<No mode set>')
351       return FailureResponse.new(last_requested_endpoint,
352                                  "Invalid openid.mode: #{mode}")
353     end
354 
355     def complete_cancel(unused_message, unused_return_to)
356       return CancelResponse.new(last_requested_endpoint)
357     end
358 
359     def complete_error(message, unused_return_to)
360       error = message.get_arg(OPENID_NS, 'error')
361       contact = message.get_arg(OPENID_NS, 'contact')
362       reference = message.get_arg(OPENID_NS, 'reference')
363 
364       return FailureResponse.new(last_requested_endpoint,
365                                  error, contact, reference)
366     end
367 
368     def complete_setup_needed(message, unused_return_to)
369       if message.is_openid1
370         return complete_invalid(message, nil)
371       else
372         setup_url = message.get_arg(OPENID2_NS, 'user_setup_url')
373         return SetupNeededResponse.new(last_requested_endpoint, setup_url)
374       end
375     end
376 
377     def complete_id_res(message, current_url)
378       if message.is_openid1
379         setup_url = message.get_arg(OPENID1_NS, 'user_setup_url')
380         if !setup_url.nil?
381           return SetupNeededResponse.new(last_requested_endpoint, setup_url)
382         end
383       end
384 
385       begin
386         idres = handle_idres(message, current_url)
387       rescue OpenIDError => why
388         return FailureResponse.new(last_requested_endpoint, why.message)
389       else
390         return SuccessResponse.new(idres.endpoint, message,
391                                      idres.signed_fields)
392       end
393     end
394   end
395 end

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

Valid XHTML 1.0! Valid CSS!