instiki/vendor/plugins/rack/doc/classes/Rack/Auth/OpenID.html

807 lines
51 KiB
HTML

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Class: Rack::Auth::OpenID</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
<script type="text/javascript">
// <![CDATA[
function popupCode( url ) {
window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
}
function toggleCode( id ) {
if ( document.getElementById )
elem = document.getElementById( id );
else if ( document.all )
elem = eval( "document.all." + id );
else
return false;
elemStyle = elem.style;
if ( elemStyle.display != "block" ) {
elemStyle.display = "block"
} else {
elemStyle.display = "none"
}
return true;
}
// Make codeblocks hidden by default
document.writeln( "<style type=\"text/css\">div.method-source-code { display: none }</style>" )
// ]]>
</script>
</head>
<body>
<div id="classHeader">
<table class="header-table">
<tr class="top-aligned-row">
<td><strong>Class</strong></td>
<td class="class-name-in-header">Rack::Auth::OpenID</td>
</tr>
<tr class="top-aligned-row">
<td><strong>In:</strong></td>
<td>
<a href="../../../files/lib/rack/auth/openid_rb.html">
lib/rack/auth/openid.rb
</a>
<br />
</td>
</tr>
<tr class="top-aligned-row">
<td><strong>Parent:</strong></td>
<td>
AbstractHandler
</td>
</tr>
</table>
</div>
<!-- banner header -->
<div id="bodyContent">
<div id="contextContent">
<div id="description">
<p>
<a href="OpenID.html">Rack::Auth::OpenID</a> provides a simple method for
permitting openid based logins. It requires the ruby-openid library from
janrain to operate, as well as a rack method of session management.
</p>
<p>
The ruby-openid home page is at <a
href="http://openidenabled.com/ruby-openid">openidenabled.com/ruby-openid</a>/.
</p>
<p>
The <a href="OpenID.html">OpenID</a> specifications can be found at <a
href="http://openid.net/specs/openid-authentication-1_1.html">openid.net/specs/openid-authentication-1_1.html</a>
and <a
href="http://openid.net/specs/openid-authentication-2_0.html">openid.net/specs/openid-authentication-2_0.html</a>.
Documentation for published <a href="OpenID.html">OpenID</a> extensions and
related topics can be found at <a
href="http://openid.net/developers/specs">openid.net/developers/specs</a>/.
</p>
<p>
It is recommended to read through the <a href="OpenID.html">OpenID</a>
spec, as well as ruby-openid&#8216;s documentation, to understand what
exactly goes on. However a setup as simple as the presented examples is
enough to provide functionality.
</p>
<p>
This library strongly intends to utilize the <a
href="OpenID.html">OpenID</a> 2.0 features of the ruby-openid library,
while maintaining <a href="OpenID.html">OpenID</a> 1.0 compatiblity.
</p>
<p>
All responses from this rack application will be 303 redirects unless an
error occurs, with the exception of an authentication request requiring an
HTML form submission.
</p>
<p>
NOTE: Extensions are not currently supported by this implimentation of the
<a href="OpenID.html">OpenID</a> rack application due to the complexity of
the current ruby-openid extension handling.
</p>
<p>
NOTE: Due to the amount of data that this library stores in the session, <a
href="../Session/Cookie.html">Rack::Session::Cookie</a> may fault.
</p>
</div>
</div>
<div id="method-list">
<h3 class="section-bar">Methods</h3>
<div class="name-list">
<a href="#M000019">add_extension</a>&nbsp;&nbsp;
<a href="#M000016">call</a>&nbsp;&nbsp;
<a href="#M000017">check</a>&nbsp;&nbsp;
<a href="#M000020">extension_namespaces</a>&nbsp;&nbsp;
<a href="#M000018">finish</a>&nbsp;&nbsp;
<a href="#M000015">new</a>&nbsp;&nbsp;
</div>
</div>
</div>
<!-- if includes -->
<div id="section">
<div id="class-list">
<h3 class="section-bar">Classes and Modules</h3>
Class <a href="OpenID/NoSession.html" class="link">Rack::Auth::OpenID::NoSession</a><br />
</div>
<div id="constants-list">
<h3 class="section-bar">Constants</h3>
<div class="name-list">
<table summary="Constants">
<tr class="top-aligned-row context-row">
<td class="context-item-name">OIDStore</td>
<td>=</td>
<td class="context-item-value">::OpenID::Store::Memory.new</td>
<td width="3em">&nbsp;</td>
<td class="context-item-desc">
Required for ruby-openid
</td>
</tr>
<tr class="top-aligned-row context-row">
<td class="context-item-name">HTML</td>
<td>=</td>
<td class="context-item-value">'&lt;html&gt;&lt;head&gt;&lt;title&gt;%s&lt;/title&gt;&lt;/head&gt;&lt;body&gt;%s&lt;/body&gt;&lt;/html&gt;'</td>
</tr>
</table>
</div>
</div>
<div id="attribute-list">
<h3 class="section-bar">Attributes</h3>
<div class="name-list">
<table>
<tr class="top-aligned-row context-row">
<td class="context-item-name">extensions</td>
<td class="context-item-value">&nbsp;[R]&nbsp;</td>
<td class="context-item-desc"></td>
</tr>
<tr class="top-aligned-row context-row">
<td class="context-item-name">options</td>
<td class="context-item-value">&nbsp;[R]&nbsp;</td>
<td class="context-item-desc"></td>
</tr>
</table>
</div>
</div>
<!-- if method_list -->
<div id="methods">
<h3 class="section-bar">Public Class methods</h3>
<div id="method-M000015" class="method-detail">
<a name="M000015"></a>
<div class="method-heading">
<a href="#M000015" class="method-signature">
<span class="method-name">new</span><span class="method-args">(realm, options={})</span>
</a>
</div>
<div class="method-description">
<p>
A Hash of options is taken as it&#8216;s single initializing argument. For
example:
</p>
<pre>
simple_oid = OpenID.new('http://mysite.com/')
return_oid = OpenID.new('http://mysite.com/', {
:return_to =&gt; 'http://mysite.com/openid'
})
page_oid = OpenID.new('http://mysite.com/',
:login_good =&gt; 'http://mysite.com/auth_good'
)
complex_oid = OpenID.new('http://mysite.com/',
:return_to =&gt; 'http://mysite.com/openid',
:login_good =&gt; 'http://mysite.com/user/preferences',
:auth_fail =&gt; [500, {'Content-Type'=&gt;'text/plain'},
'Unable to negotiate with foreign server.'],
:immediate =&gt; true,
:extensions =&gt; {
::OpenID::SReg =&gt; [['email'],['nickname']]
}
)
</pre>
<h1>Arguments</h1>
<p>
The first argument is the realm, identifying the site they are trusting
with their identity. This is required.
</p>
<p>
NOTE: In <a href="OpenID.html">OpenID</a> 1.x, the realm or trust_root is
optional and the return_to url is required. As this library strives tward
ruby-openid 2.0, and <a href="OpenID.html">OpenID</a> 2.0 compatibiliy, the
realm is required and return_to is optional. However, this implimentation
is still backwards compatible with <a href="OpenID.html">OpenID</a> 1.0
servers.
</p>
<p>
The optional second argument is a hash of options.
</p>
<h2>Options</h2>
<p>
<tt>:return_to</tt> defines the url to return to after the client
authenticates with the openid service provider. This url should point to
where <a href="OpenID.html">Rack::Auth::OpenID</a> is mounted. If
<tt>:return_to</tt> is not provided, :return_to will be the current url
including all query parameters.
</p>
<p>
<tt>:session_key</tt> defines the key to the session hash in the env. It
defaults to &#8216;rack.session&#8217;.
</p>
<p>
<tt>:openid_param</tt> defines at what key in the request parameters to
find the identifier to resolve. As per the 2.0 spec, the default is
&#8216;openid_identifier&#8217;.
</p>
<p>
<tt>:immediate</tt> as true will make immediate type of requests the
default. See <a href="OpenID.html">OpenID</a> specification documentation.
</p>
<h3>URL options</h3>
<p>
<tt>:login_good</tt> is the url to go to after the authentication process
has completed.
</p>
<p>
<tt>:login_fail</tt> is the url to go to after the authentication process
has failed.
</p>
<p>
<tt>:login_quit</tt> is the url to go to after the authentication process
has been cancelled.
</p>
<h3><a href="../Response.html">Response</a> options</h3>
<p>
<tt>:no_session</tt> should be a rack response to be returned if no or an
incompatible session is found.
</p>
<p>
<tt>:auth_fail</tt> should be a rack response to be returned if an
OpenID::DiscoveryFailure occurs. This is typically due to being unable to
access the identity url or identity server.
</p>
<p>
<tt>:error</tt> should be a rack response to return if any other generic
error would occur and <tt>options[:catch_errors]</tt> is true.
</p>
<h3>Extensions</h3>
<p>
<tt>:extensions</tt> should be a hash of openid extension implementations.
The key should be the extension main module, the value should be an array
of arguments for <a
href="../Request.html#M000154">extension::Request.new</a>
</p>
<p>
The hash is iterated over and passed to <a
href="OpenID.html#M000019">add_extension</a> for processing. Please see <a
href="OpenID.html#M000019">add_extension</a> for further documentation.
</p>
<p><a class="source-toggle" href="#"
onclick="toggleCode('M000015-source');return false;">[Source]</a></p>
<div class="method-source-code" id="M000015-source">
<pre>
<span class="ruby-comment cmt"># File lib/rack/auth/openid.rb, line 137</span>
137: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">initialize</span>(<span class="ruby-identifier">realm</span>, <span class="ruby-identifier">options</span>={})
138: <span class="ruby-ivar">@realm</span> = <span class="ruby-identifier">realm</span>
139: <span class="ruby-identifier">realm</span> = <span class="ruby-constant">URI</span>(<span class="ruby-identifier">realm</span>)
140: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">realm</span>.<span class="ruby-identifier">path</span>.<span class="ruby-identifier">empty?</span>
141: <span class="ruby-identifier">raise</span> <span class="ruby-constant">ArgumentError</span>, <span class="ruby-node">&quot;Invalid realm path: '#{realm.path}'&quot;</span>
142: <span class="ruby-keyword kw">elsif</span> <span class="ruby-keyword kw">not</span> <span class="ruby-identifier">realm</span>.<span class="ruby-identifier">absolute?</span>
143: <span class="ruby-identifier">raise</span> <span class="ruby-constant">ArgumentError</span>, <span class="ruby-node">&quot;Realm '#{@realm}' not absolute&quot;</span>
144: <span class="ruby-keyword kw">end</span>
145:
146: [<span class="ruby-identifier">:return_to</span>, <span class="ruby-identifier">:login_good</span>, <span class="ruby-identifier">:login_fail</span>, <span class="ruby-identifier">:login_quit</span>].<span class="ruby-identifier">each</span> <span class="ruby-keyword kw">do</span> <span class="ruby-operator">|</span><span class="ruby-identifier">key</span><span class="ruby-operator">|</span>
147: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">options</span>.<span class="ruby-identifier">key?</span> <span class="ruby-identifier">key</span> <span class="ruby-keyword kw">and</span> <span class="ruby-identifier">luri</span> = <span class="ruby-constant">URI</span>(<span class="ruby-identifier">options</span>[<span class="ruby-identifier">key</span>])
148: <span class="ruby-keyword kw">if</span> <span class="ruby-operator">!</span><span class="ruby-identifier">luri</span>.<span class="ruby-identifier">absolute?</span>
149: <span class="ruby-identifier">raise</span> <span class="ruby-constant">ArgumentError</span>, <span class="ruby-node">&quot;:#{key} is not an absolute uri: '#{luri}'&quot;</span>
150: <span class="ruby-keyword kw">end</span>
151: <span class="ruby-keyword kw">end</span>
152: <span class="ruby-keyword kw">end</span>
153:
154: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">options</span>[<span class="ruby-identifier">:return_to</span>] <span class="ruby-keyword kw">and</span> <span class="ruby-identifier">ruri</span> = <span class="ruby-constant">URI</span>(<span class="ruby-identifier">options</span>[<span class="ruby-identifier">:return_to</span>])
155: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">ruri</span>.<span class="ruby-identifier">path</span>.<span class="ruby-identifier">empty?</span>
156: <span class="ruby-identifier">raise</span> <span class="ruby-constant">ArgumentError</span>, <span class="ruby-node">&quot;Invalid return_to path: '#{ruri.path}'&quot;</span>
157: <span class="ruby-keyword kw">elsif</span> <span class="ruby-identifier">realm</span>.<span class="ruby-identifier">path</span> <span class="ruby-operator">!=</span> <span class="ruby-identifier">ruri</span>.<span class="ruby-identifier">path</span>[<span class="ruby-value">0</span>, <span class="ruby-identifier">realm</span>.<span class="ruby-identifier">path</span>.<span class="ruby-identifier">size</span>]
158: <span class="ruby-identifier">raise</span> <span class="ruby-constant">ArgumentError</span>, <span class="ruby-value str">'return_to not within realm.'</span> \
159: <span class="ruby-keyword kw">end</span>
160: <span class="ruby-keyword kw">end</span>
161:
162: <span class="ruby-comment cmt"># TODO: extension support</span>
163: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">extensions</span> = <span class="ruby-identifier">options</span>.<span class="ruby-identifier">delete</span>(<span class="ruby-identifier">:extensions</span>)
164: <span class="ruby-identifier">extensions</span>.<span class="ruby-identifier">each</span> <span class="ruby-keyword kw">do</span> <span class="ruby-operator">|</span><span class="ruby-identifier">ext</span>, <span class="ruby-identifier">args</span><span class="ruby-operator">|</span>
165: <span class="ruby-identifier">add_extension</span> <span class="ruby-identifier">ext</span>, <span class="ruby-operator">*</span><span class="ruby-identifier">args</span>
166: <span class="ruby-keyword kw">end</span>
167: <span class="ruby-keyword kw">end</span>
168:
169: <span class="ruby-ivar">@options</span> = {
170: <span class="ruby-identifier">:session_key</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-value str">'rack.session'</span>,
171: <span class="ruby-identifier">:openid_param</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-value str">'openid_identifier'</span>,
172: <span class="ruby-comment cmt">#:return_to, :login_good, :login_fail, :login_quit</span>
173: <span class="ruby-comment cmt">#:no_session, :auth_fail, :error</span>
174: <span class="ruby-identifier">:store</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-constant">OIDStore</span>,
175: <span class="ruby-identifier">:immediate</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-keyword kw">false</span>,
176: <span class="ruby-identifier">:anonymous</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-keyword kw">false</span>,
177: <span class="ruby-identifier">:catch_errors</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-keyword kw">false</span>
178: }.<span class="ruby-identifier">merge</span>(<span class="ruby-identifier">options</span>)
179: <span class="ruby-ivar">@extensions</span> = {}
180: <span class="ruby-keyword kw">end</span>
</pre>
</div>
</div>
</div>
<h3 class="section-bar">Public Instance methods</h3>
<div id="method-M000019" class="method-detail">
<a name="M000019"></a>
<div class="method-heading">
<a href="#M000019" class="method-signature">
<span class="method-name">add_extension</span><span class="method-args">(ext, *args)</span>
</a>
</div>
<div class="method-description">
<p>
The first argument should be the main extension module. The extension
module should contain the constants:
</p>
<pre>
* class Request, with OpenID::Extension as an ancestor
* class Response, with OpenID::Extension as an ancestor
* string NS_URI, which defines the namespace of the extension, should
be an absolute http uri
</pre>
<p>
All trailing arguments will be passed to <a
href="../Request.html#M000154">extension::Request.new</a> in <a
href="OpenID.html#M000017">check</a>. The openid response will be passed to
extension::Response#from_success_response, get_extension_args will be
called on the result to attain the gathered data.
</p>
<p>
This method returns the key at which the response data will be found in the
session, which is the namespace uri by default.
</p>
<p><a class="source-toggle" href="#"
onclick="toggleCode('M000019-source');return false;">[Source]</a></p>
<div class="method-source-code" id="M000019-source">
<pre>
<span class="ruby-comment cmt"># File lib/rack/auth/openid.rb, line 402</span>
402: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">add_extension</span> <span class="ruby-identifier">ext</span>, <span class="ruby-operator">*</span><span class="ruby-identifier">args</span>
403: <span class="ruby-keyword kw">if</span> <span class="ruby-keyword kw">not</span> <span class="ruby-identifier">ext</span>.<span class="ruby-identifier">is_a?</span> <span class="ruby-constant">Module</span>
404: <span class="ruby-identifier">raise</span> <span class="ruby-constant">TypeError</span>, <span class="ruby-node">&quot;#{ext.inspect} is not a module&quot;</span>
405: <span class="ruby-keyword kw">elsif</span> <span class="ruby-operator">!</span>(<span class="ruby-identifier">m</span> = <span class="ruby-node">%w'Request Response NS_URI'</span> <span class="ruby-operator">-</span>
406: <span class="ruby-identifier">ext</span>.<span class="ruby-identifier">constants</span>.<span class="ruby-identifier">map</span>{ <span class="ruby-operator">|</span><span class="ruby-identifier">c</span><span class="ruby-operator">|</span> <span class="ruby-identifier">c</span>.<span class="ruby-identifier">to_s</span> }).<span class="ruby-identifier">empty?</span>
407: <span class="ruby-identifier">raise</span> <span class="ruby-constant">ArgumentError</span>, <span class="ruby-node">&quot;#{ext.inspect} missing #{m*', '}&quot;</span>
408: <span class="ruby-keyword kw">end</span>
409:
410: <span class="ruby-identifier">consts</span> = [<span class="ruby-identifier">ext</span><span class="ruby-operator">::</span><span class="ruby-constant">Request</span>, <span class="ruby-identifier">ext</span><span class="ruby-operator">::</span><span class="ruby-constant">Response</span>]
411:
412: <span class="ruby-keyword kw">if</span> <span class="ruby-keyword kw">not</span> <span class="ruby-identifier">consts</span>.<span class="ruby-identifier">all?</span>{<span class="ruby-operator">|</span><span class="ruby-identifier">c</span><span class="ruby-operator">|</span> <span class="ruby-identifier">c</span>.<span class="ruby-identifier">is_a?</span> <span class="ruby-constant">Class</span> }
413: <span class="ruby-identifier">raise</span> <span class="ruby-constant">TypeError</span>, <span class="ruby-node">&quot;#{ext.inspect}'s Request or Response is not a class&quot;</span>
414: <span class="ruby-keyword kw">elsif</span> <span class="ruby-keyword kw">not</span> <span class="ruby-identifier">consts</span>.<span class="ruby-identifier">all?</span>{<span class="ruby-operator">|</span><span class="ruby-identifier">c</span><span class="ruby-operator">|</span> <span class="ruby-operator">::</span><span class="ruby-constant">OpenID</span><span class="ruby-operator">::</span><span class="ruby-constant">Extension</span> <span class="ruby-operator">&gt;</span> <span class="ruby-identifier">c</span> }
415: <span class="ruby-identifier">raise</span> <span class="ruby-constant">ArgumentError</span>, <span class="ruby-node">&quot;#{ext.inspect}'s Request or Response not a decendant of OpenID::Extension&quot;</span>
416: <span class="ruby-keyword kw">end</span>
417:
418: <span class="ruby-keyword kw">if</span> <span class="ruby-keyword kw">not</span> <span class="ruby-identifier">ext</span><span class="ruby-operator">::</span><span class="ruby-constant">NS_URI</span>.<span class="ruby-identifier">is_a?</span> <span class="ruby-constant">String</span>
419: <span class="ruby-identifier">raise</span> <span class="ruby-constant">TypeError</span>, <span class="ruby-node">&quot;#{ext.inspect}'s NS_URI is not a string&quot;</span>
420: <span class="ruby-keyword kw">elsif</span> <span class="ruby-keyword kw">not</span> <span class="ruby-identifier">uri</span> = <span class="ruby-constant">URI</span>(<span class="ruby-identifier">ext</span><span class="ruby-operator">::</span><span class="ruby-constant">NS_URI</span>)
421: <span class="ruby-identifier">raise</span> <span class="ruby-constant">ArgumentError</span>, <span class="ruby-node">&quot;#{ext.inspect}'s NS_URI is not a valid uri&quot;</span>
422: <span class="ruby-keyword kw">elsif</span> <span class="ruby-keyword kw">not</span> <span class="ruby-identifier">uri</span>.<span class="ruby-identifier">scheme</span> <span class="ruby-operator">=~</span> <span class="ruby-regexp re">/^https?$/</span>
423: <span class="ruby-identifier">raise</span> <span class="ruby-constant">ArgumentError</span>, <span class="ruby-node">&quot;#{ext.inspect}'s NS_URI is not an http uri&quot;</span>
424: <span class="ruby-keyword kw">elsif</span> <span class="ruby-keyword kw">not</span> <span class="ruby-identifier">uri</span>.<span class="ruby-identifier">absolute?</span>
425: <span class="ruby-identifier">raise</span> <span class="ruby-constant">ArgumentError</span>, <span class="ruby-node">&quot;#{ext.inspect}'s NS_URI is not and absolute uri&quot;</span>
426: <span class="ruby-keyword kw">end</span>
427: <span class="ruby-ivar">@extensions</span>[<span class="ruby-identifier">ext</span>] = <span class="ruby-identifier">args</span>
428: <span class="ruby-keyword kw">return</span> <span class="ruby-identifier">ext</span><span class="ruby-operator">::</span><span class="ruby-constant">NS_URI</span>
429: <span class="ruby-keyword kw">end</span>
</pre>
</div>
</div>
</div>
<div id="method-M000016" class="method-detail">
<a name="M000016"></a>
<div class="method-heading">
<a href="#M000016" class="method-signature">
<span class="method-name">call</span><span class="method-args">(env)</span>
</a>
</div>
<div class="method-description">
<p>
It sets up and uses session data at <tt>:openid</tt> within the session. It
sets up the ::OpenID::Consumer using the store specified by
<tt>options[:store]</tt>.
</p>
<p>
If the parameter specified by <tt>options[:openid_param]</tt> is present,
processing is passed to <a href="OpenID.html#M000017">check</a> and the
result is returned.
</p>
<p>
If the parameter &#8216;openid.mode&#8217; is set, implying a followup from
the openid server, processing is passed to <a
href="OpenID.html#M000018">finish</a> and the result is returned.
</p>
<p>
If neither of these conditions are met, a 400 error is returned.
</p>
<p>
If an error is thrown and <tt>options[:catch_errors]</tt> is false, the
exception will be reraised. Otherwise a 500 error is returned.
</p>
<p><a class="source-toggle" href="#"
onclick="toggleCode('M000016-source');return false;">[Source]</a></p>
<div class="method-source-code" id="M000016-source">
<pre>
<span class="ruby-comment cmt"># File lib/rack/auth/openid.rb, line 199</span>
199: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">call</span>(<span class="ruby-identifier">env</span>)
200: <span class="ruby-identifier">env</span>[<span class="ruby-value str">'rack.auth.openid'</span>] = <span class="ruby-keyword kw">self</span>
201: <span class="ruby-identifier">session</span> = <span class="ruby-identifier">env</span>[<span class="ruby-ivar">@options</span>[<span class="ruby-identifier">:session_key</span>]]
202: <span class="ruby-keyword kw">unless</span> <span class="ruby-identifier">session</span> <span class="ruby-keyword kw">and</span> <span class="ruby-identifier">session</span>.<span class="ruby-identifier">is_a?</span> <span class="ruby-constant">Hash</span>
203: <span class="ruby-identifier">raise</span>(<span class="ruby-constant">NoSession</span>, <span class="ruby-value str">'No compatible session'</span>)
204: <span class="ruby-keyword kw">end</span>
205: <span class="ruby-comment cmt"># let us work in our own namespace...</span>
206: <span class="ruby-identifier">session</span> = (<span class="ruby-identifier">session</span>[<span class="ruby-identifier">:openid</span>] <span class="ruby-operator">||=</span> {})
207: <span class="ruby-keyword kw">unless</span> <span class="ruby-identifier">session</span> <span class="ruby-keyword kw">and</span> <span class="ruby-identifier">session</span>.<span class="ruby-identifier">is_a?</span> <span class="ruby-constant">Hash</span>
208: <span class="ruby-identifier">raise</span>(<span class="ruby-constant">NoSession</span>, <span class="ruby-value str">'Incompatible session'</span>)
209: <span class="ruby-keyword kw">end</span>
210:
211: <span class="ruby-identifier">request</span> = <span class="ruby-constant">Rack</span><span class="ruby-operator">::</span><span class="ruby-constant">Request</span>.<span class="ruby-identifier">new</span> <span class="ruby-identifier">env</span>
212: <span class="ruby-identifier">consumer</span> = <span class="ruby-operator">::</span><span class="ruby-constant">OpenID</span><span class="ruby-operator">::</span><span class="ruby-constant">Consumer</span>.<span class="ruby-identifier">new</span> <span class="ruby-identifier">session</span>, <span class="ruby-ivar">@options</span>[<span class="ruby-identifier">:store</span>]
213:
214: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">request</span>.<span class="ruby-identifier">params</span>[<span class="ruby-value str">'openid.mode'</span>]
215: <span class="ruby-identifier">finish</span> <span class="ruby-identifier">consumer</span>, <span class="ruby-identifier">session</span>, <span class="ruby-identifier">request</span>
216: <span class="ruby-keyword kw">elsif</span> <span class="ruby-identifier">request</span>.<span class="ruby-identifier">params</span>[<span class="ruby-ivar">@options</span>[<span class="ruby-identifier">:openid_param</span>]]
217: <span class="ruby-identifier">check</span> <span class="ruby-identifier">consumer</span>, <span class="ruby-identifier">session</span>, <span class="ruby-identifier">request</span>
218: <span class="ruby-keyword kw">else</span>
219: <span class="ruby-identifier">env</span>[<span class="ruby-value str">'rack.errors'</span>].<span class="ruby-identifier">puts</span> <span class="ruby-value str">&quot;No valid params provided.&quot;</span>
220: <span class="ruby-identifier">bad_request</span>
221: <span class="ruby-keyword kw">end</span>
222: <span class="ruby-keyword kw">rescue</span> <span class="ruby-constant">NoSession</span>
223: <span class="ruby-identifier">env</span>[<span class="ruby-value str">'rack.errors'</span>].<span class="ruby-identifier">puts</span>(<span class="ruby-identifier">$!</span>.<span class="ruby-identifier">message</span>, <span class="ruby-operator">*</span><span class="ruby-identifier">$@</span>)
224:
225: <span class="ruby-ivar">@options</span>. <span class="ruby-comment cmt">### Missing or incompatible session</span>
226: <span class="ruby-identifier">fetch</span> <span class="ruby-identifier">:no_session</span>, [ <span class="ruby-value">500</span>,
227: {<span class="ruby-value str">'Content-Type'</span>=<span class="ruby-operator">&gt;</span><span class="ruby-value str">'text/plain'</span>},
228: <span class="ruby-identifier">$!</span>.<span class="ruby-identifier">message</span> ]
229: <span class="ruby-keyword kw">rescue</span>
230: <span class="ruby-identifier">env</span>[<span class="ruby-value str">'rack.errors'</span>].<span class="ruby-identifier">puts</span>(<span class="ruby-identifier">$!</span>.<span class="ruby-identifier">message</span>, <span class="ruby-operator">*</span><span class="ruby-identifier">$@</span>)
231:
232: <span class="ruby-keyword kw">if</span> <span class="ruby-keyword kw">not</span> <span class="ruby-ivar">@options</span>[<span class="ruby-identifier">:catch_error</span>]
233: <span class="ruby-identifier">raise</span>(<span class="ruby-identifier">$!</span>)
234: <span class="ruby-keyword kw">end</span>
235: <span class="ruby-ivar">@options</span>.
236: <span class="ruby-identifier">fetch</span> <span class="ruby-identifier">:error</span>, [ <span class="ruby-value">500</span>,
237: {<span class="ruby-value str">'Content-Type'</span>=<span class="ruby-operator">&gt;</span><span class="ruby-value str">'text/plain'</span>},
238: <span class="ruby-value str">'OpenID has encountered an error.'</span> ]
239: <span class="ruby-keyword kw">end</span>
</pre>
</div>
</div>
</div>
<div id="method-M000017" class="method-detail">
<a name="M000017"></a>
<div class="method-heading">
<a href="#M000017" class="method-signature">
<span class="method-name">check</span><span class="method-args">(consumer, session, req)</span>
</a>
</div>
<div class="method-description">
<p>
As the first part of <a href="OpenID.html">OpenID</a> consumer action, <a
href="OpenID.html#M000017">check</a> retrieves the data required for
completion.
</p>
<ul>
<li><tt>session[:openid][:openid_param]</tt> is set to the submitted identifier
to be authenticated.
</li>
<li><tt>session[:openid][:site_return]</tt> is set as the request&#8216;s
HTTP_REFERER, unless already set.
</li>
<li><tt><a href="http://'rack.auth.openid.request'">env</a></tt> is the openid
checkid request instance.
</li>
</ul>
<p><a class="source-toggle" href="#"
onclick="toggleCode('M000017-source');return false;">[Source]</a></p>
<div class="method-source-code" id="M000017-source">
<pre>
<span class="ruby-comment cmt"># File lib/rack/auth/openid.rb, line 250</span>
250: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">check</span>(<span class="ruby-identifier">consumer</span>, <span class="ruby-identifier">session</span>, <span class="ruby-identifier">req</span>)
251: <span class="ruby-identifier">session</span>[<span class="ruby-identifier">:openid_param</span>] = <span class="ruby-identifier">req</span>.<span class="ruby-identifier">params</span>[<span class="ruby-ivar">@options</span>[<span class="ruby-identifier">:openid_param</span>]]
252: <span class="ruby-identifier">oid</span> = <span class="ruby-identifier">consumer</span>.<span class="ruby-identifier">begin</span>(<span class="ruby-identifier">session</span>[<span class="ruby-identifier">:openid_param</span>], <span class="ruby-ivar">@options</span>[<span class="ruby-identifier">:anonymous</span>])
253: <span class="ruby-identifier">pp</span> <span class="ruby-identifier">oid</span> <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">$DEBUG</span>
254: <span class="ruby-identifier">req</span>.<span class="ruby-identifier">env</span>[<span class="ruby-value str">'rack.auth.openid.request'</span>] = <span class="ruby-identifier">oid</span>
255:
256: <span class="ruby-identifier">session</span>[<span class="ruby-identifier">:site_return</span>] <span class="ruby-operator">||=</span> <span class="ruby-identifier">req</span>.<span class="ruby-identifier">env</span>[<span class="ruby-value str">'HTTP_REFERER'</span>]
257:
258: <span class="ruby-comment cmt"># SETUP_NEEDED check!</span>
259: <span class="ruby-comment cmt"># see OpenID::Consumer::CheckIDRequest docs</span>
260: <span class="ruby-identifier">query_args</span> = [<span class="ruby-ivar">@realm</span>, <span class="ruby-operator">*</span><span class="ruby-ivar">@options</span>.<span class="ruby-identifier">values_at</span>(<span class="ruby-identifier">:return_to</span>, <span class="ruby-identifier">:immediate</span>)]
261: <span class="ruby-identifier">query_args</span>[<span class="ruby-value">1</span>] <span class="ruby-operator">||=</span> <span class="ruby-identifier">req</span>.<span class="ruby-identifier">url</span>
262: <span class="ruby-identifier">query_args</span>[<span class="ruby-value">2</span>] = <span class="ruby-keyword kw">false</span> <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">session</span>.<span class="ruby-identifier">key?</span> <span class="ruby-identifier">:setup_needed</span>
263: <span class="ruby-identifier">pp</span> <span class="ruby-identifier">query_args</span> <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">$DEBUG</span>
264:
265: <span class="ruby-comment cmt">## Extension support</span>
266: <span class="ruby-identifier">extensions</span>.<span class="ruby-identifier">each</span> <span class="ruby-keyword kw">do</span> <span class="ruby-operator">|</span><span class="ruby-identifier">ext</span>,<span class="ruby-identifier">args</span><span class="ruby-operator">|</span>
267: <span class="ruby-identifier">oid</span>.<span class="ruby-identifier">add_extension</span> <span class="ruby-identifier">ext</span><span class="ruby-operator">::</span><span class="ruby-constant">Request</span>.<span class="ruby-identifier">new</span>(<span class="ruby-operator">*</span><span class="ruby-identifier">args</span>)
268: <span class="ruby-keyword kw">end</span>
269:
270: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">oid</span>.<span class="ruby-identifier">send_redirect?</span>(<span class="ruby-operator">*</span><span class="ruby-identifier">query_args</span>)
271: <span class="ruby-identifier">redirect</span> = <span class="ruby-identifier">oid</span>.<span class="ruby-identifier">redirect_url</span>(<span class="ruby-operator">*</span><span class="ruby-identifier">query_args</span>)
272: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">$DEBUG</span>
273: <span class="ruby-identifier">pp</span> <span class="ruby-identifier">redirect</span>
274: <span class="ruby-identifier">pp</span> <span class="ruby-constant">Rack</span><span class="ruby-operator">::</span><span class="ruby-constant">Utils</span>.<span class="ruby-identifier">parse_query</span>(<span class="ruby-constant">URI</span>(<span class="ruby-identifier">redirect</span>).<span class="ruby-identifier">query</span>)
275: <span class="ruby-keyword kw">end</span>
276: [ <span class="ruby-value">303</span>, {<span class="ruby-value str">'Location'</span>=<span class="ruby-operator">&gt;</span><span class="ruby-identifier">redirect</span>}, [] ]
277: <span class="ruby-keyword kw">else</span>
278: <span class="ruby-comment cmt"># check on 'action' option.</span>
279: <span class="ruby-identifier">formbody</span> = <span class="ruby-identifier">oid</span>.<span class="ruby-identifier">form_markup</span>(<span class="ruby-operator">*</span><span class="ruby-identifier">query_args</span>)
280: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">$DEBUG</span>
281: <span class="ruby-identifier">pp</span> <span class="ruby-identifier">formbody</span>
282: <span class="ruby-keyword kw">end</span>
283: <span class="ruby-identifier">body</span> = <span class="ruby-constant">HTML</span> <span class="ruby-operator">%</span> [<span class="ruby-value str">'Confirm...'</span>, <span class="ruby-identifier">formbody</span>]
284: [ <span class="ruby-value">200</span>, {<span class="ruby-value str">'Content-Type'</span>=<span class="ruby-operator">&gt;</span><span class="ruby-value str">'text/html'</span>}, <span class="ruby-identifier">body</span>.<span class="ruby-identifier">to_a</span> ]
285: <span class="ruby-keyword kw">end</span>
286: <span class="ruby-keyword kw">rescue</span> <span class="ruby-operator">::</span><span class="ruby-constant">OpenID</span><span class="ruby-operator">::</span><span class="ruby-constant">DiscoveryFailure</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-identifier">e</span>
287: <span class="ruby-comment cmt"># thrown from inside OpenID::Consumer#begin by yadis stuff</span>
288: <span class="ruby-identifier">req</span>.<span class="ruby-identifier">env</span>[<span class="ruby-value str">'rack.errors'</span>].<span class="ruby-identifier">puts</span>(<span class="ruby-identifier">$!</span>.<span class="ruby-identifier">message</span>, <span class="ruby-operator">*</span><span class="ruby-identifier">$@</span>)
289:
290: <span class="ruby-ivar">@options</span>. <span class="ruby-comment cmt">### Foreign server failed</span>
291: <span class="ruby-identifier">fetch</span> <span class="ruby-identifier">:auth_fail</span>, [ <span class="ruby-value">503</span>,
292: {<span class="ruby-value str">'Content-Type'</span>=<span class="ruby-operator">&gt;</span><span class="ruby-value str">'text/plain'</span>},
293: <span class="ruby-value str">'Foreign server failure.'</span> ]
294: <span class="ruby-keyword kw">end</span>
</pre>
</div>
</div>
</div>
<div id="method-M000020" class="method-detail">
<a name="M000020"></a>
<div class="method-heading">
<a href="#M000020" class="method-signature">
<span class="method-name">extension_namespaces</span><span class="method-args">()</span>
</a>
</div>
<div class="method-description">
<p>
A conveniance method that returns the namespace of all current extensions
used by this instance.
</p>
<p><a class="source-toggle" href="#"
onclick="toggleCode('M000020-source');return false;">[Source]</a></p>
<div class="method-source-code" id="M000020-source">
<pre>
<span class="ruby-comment cmt"># File lib/rack/auth/openid.rb, line 433</span>
433: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">extension_namespaces</span>
434: <span class="ruby-ivar">@extensions</span>.<span class="ruby-identifier">keys</span>.<span class="ruby-identifier">map</span>{<span class="ruby-operator">|</span><span class="ruby-identifier">e</span><span class="ruby-operator">|</span><span class="ruby-identifier">e</span><span class="ruby-operator">::</span><span class="ruby-constant">NS_URI</span>}
435: <span class="ruby-keyword kw">end</span>
</pre>
</div>
</div>
</div>
<div id="method-M000018" class="method-detail">
<a name="M000018"></a>
<div class="method-heading">
<a href="#M000018" class="method-signature">
<span class="method-name">finish</span><span class="method-args">(consumer, session, req)</span>
</a>
</div>
<div class="method-description">
<p>
This is the final portion of authentication. Unless any errors outside of
specification occur, a 303 redirect will be returned with Location
determined by the <a href="OpenID.html">OpenID</a> response type. If none
of the response type :login_* urls are set, the redirect will be set to
<tt>session[:openid][:site_return]</tt>. If
<tt>session[:openid][:site_return]</tt> is unset, the realm will be used.
</p>
<p>
Any messages from <a href="OpenID.html">OpenID</a>&#8216;s response are
appended to the 303 response body.
</p>
<p>
Data gathered from extensions are stored in session[:openid] with the
extension&#8216;s namespace uri as the key.
</p>
<ul>
<li><tt><a href="http://'rack.auth.openid.response'">env</a></tt> is the openid
response.
</li>
</ul>
<p>
The four valid possible outcomes are:
</p>
<ul>
<li>failure: <tt>options[:login_fail]</tt> or <tt>session[:site_return]</tt> or
the realm
<ul>
<li><tt>session[:openid]</tt> is cleared and any messages are send to
rack.errors
</li>
<li><tt>session[:openid][&#8216;authenticated&#8217;]</tt> is <tt>false</tt>
</li>
</ul>
</li>
<li>success: <tt>options[:login_good]</tt> or <tt>session[:site_return]</tt> or
the realm
<ul>
<li><tt>session[:openid]</tt> is cleared
</li>
<li><tt>session[:openid][&#8216;authenticated&#8217;]</tt> is <tt>true</tt>
</li>
<li><tt>session[:openid][&#8216;identity&#8217;]</tt> is the actual identifier
</li>
<li><tt>session[:openid][&#8216;identifier&#8217;]</tt> is the pretty
identifier
</li>
</ul>
</li>
<li>cancel: <tt>options[:login_good]</tt> or <tt>session[:site_return]</tt> or
the realm
<ul>
<li><tt>session[:openid]</tt> is cleared
</li>
<li><tt>session[:openid][&#8216;authenticated&#8217;]</tt> is <tt>false</tt>
</li>
</ul>
</li>
<li>setup_needed: resubmits the authentication request. A flag is set for
non-immediate handling.
<ul>
<li><tt>session[:openid][:setup_needed]</tt> is set to <tt>true</tt>, which
will prevent immediate style openid authentication.
</li>
</ul>
</li>
</ul>
<p><a class="source-toggle" href="#"
onclick="toggleCode('M000018-source');return false;">[Source]</a></p>
<div class="method-source-code" id="M000018-source">
<pre>
<span class="ruby-comment cmt"># File lib/rack/auth/openid.rb, line 332</span>
332: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">finish</span>(<span class="ruby-identifier">consumer</span>, <span class="ruby-identifier">session</span>, <span class="ruby-identifier">req</span>)
333: <span class="ruby-identifier">oid</span> = <span class="ruby-identifier">consumer</span>.<span class="ruby-identifier">complete</span>(<span class="ruby-identifier">req</span>.<span class="ruby-identifier">params</span>, <span class="ruby-identifier">req</span>.<span class="ruby-identifier">url</span>)
334: <span class="ruby-identifier">pp</span> <span class="ruby-identifier">oid</span> <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">$DEBUG</span>
335: <span class="ruby-identifier">req</span>.<span class="ruby-identifier">env</span>[<span class="ruby-value str">'rack.auth.openid.response'</span>] = <span class="ruby-identifier">oid</span>
336:
337: <span class="ruby-identifier">goto</span> = <span class="ruby-identifier">session</span>.<span class="ruby-identifier">fetch</span> <span class="ruby-identifier">:site_return</span>, <span class="ruby-ivar">@realm</span>
338: <span class="ruby-identifier">body</span> = []
339:
340: <span class="ruby-keyword kw">case</span> <span class="ruby-identifier">oid</span>.<span class="ruby-identifier">status</span>
341: <span class="ruby-keyword kw">when</span> <span class="ruby-operator">::</span><span class="ruby-constant">OpenID</span><span class="ruby-operator">::</span><span class="ruby-constant">Consumer</span><span class="ruby-operator">::</span><span class="ruby-constant">FAILURE</span>
342: <span class="ruby-identifier">session</span>.<span class="ruby-identifier">clear</span>
343: <span class="ruby-identifier">session</span>[<span class="ruby-value str">'authenticated'</span>] = <span class="ruby-keyword kw">false</span>
344: <span class="ruby-identifier">req</span>.<span class="ruby-identifier">env</span>[<span class="ruby-value str">'rack.errors'</span>].<span class="ruby-identifier">puts</span> <span class="ruby-identifier">oid</span>.<span class="ruby-identifier">message</span>
345:
346: <span class="ruby-identifier">goto</span> = <span class="ruby-ivar">@options</span>[<span class="ruby-identifier">:login_fail</span>] <span class="ruby-keyword kw">if</span> <span class="ruby-ivar">@options</span>.<span class="ruby-identifier">key?</span> <span class="ruby-identifier">:login_fail</span>
347: <span class="ruby-identifier">body</span> <span class="ruby-operator">&lt;&lt;</span> <span class="ruby-value str">&quot;Authentication unsuccessful.\n&quot;</span>
348: <span class="ruby-keyword kw">when</span> <span class="ruby-operator">::</span><span class="ruby-constant">OpenID</span><span class="ruby-operator">::</span><span class="ruby-constant">Consumer</span><span class="ruby-operator">::</span><span class="ruby-constant">SUCCESS</span>
349: <span class="ruby-identifier">session</span>.<span class="ruby-identifier">clear</span>
350:
351: <span class="ruby-comment cmt">## Extension support</span>
352: <span class="ruby-identifier">extensions</span>.<span class="ruby-identifier">each</span> <span class="ruby-keyword kw">do</span> <span class="ruby-operator">|</span><span class="ruby-identifier">ext</span>, <span class="ruby-identifier">args</span><span class="ruby-operator">|</span>
353: <span class="ruby-identifier">session</span>[<span class="ruby-identifier">ext</span><span class="ruby-operator">::</span><span class="ruby-constant">NS_URI</span>] = <span class="ruby-identifier">ext</span><span class="ruby-operator">::</span><span class="ruby-constant">Response</span>.
354: <span class="ruby-identifier">from_success_response</span>(<span class="ruby-identifier">oid</span>).
355: <span class="ruby-identifier">get_extension_args</span>
356: <span class="ruby-keyword kw">end</span>
357:
358: <span class="ruby-identifier">session</span>[<span class="ruby-value str">'authenticated'</span>] = <span class="ruby-keyword kw">true</span>
359: <span class="ruby-comment cmt"># Value for unique identification and such</span>
360: <span class="ruby-identifier">session</span>[<span class="ruby-value str">'identity'</span>] = <span class="ruby-identifier">oid</span>.<span class="ruby-identifier">identity_url</span>
361: <span class="ruby-comment cmt"># Value for display and UI labels</span>
362: <span class="ruby-identifier">session</span>[<span class="ruby-value str">'identifier'</span>] = <span class="ruby-identifier">oid</span>.<span class="ruby-identifier">display_identifier</span>
363:
364: <span class="ruby-identifier">goto</span> = <span class="ruby-ivar">@options</span>[<span class="ruby-identifier">:login_good</span>] <span class="ruby-keyword kw">if</span> <span class="ruby-ivar">@options</span>.<span class="ruby-identifier">key?</span> <span class="ruby-identifier">:login_good</span>
365: <span class="ruby-identifier">body</span> <span class="ruby-operator">&lt;&lt;</span> <span class="ruby-value str">&quot;Authentication successful.\n&quot;</span>
366: <span class="ruby-keyword kw">when</span> <span class="ruby-operator">::</span><span class="ruby-constant">OpenID</span><span class="ruby-operator">::</span><span class="ruby-constant">Consumer</span><span class="ruby-operator">::</span><span class="ruby-constant">CANCEL</span>
367: <span class="ruby-identifier">session</span>.<span class="ruby-identifier">clear</span>
368: <span class="ruby-identifier">session</span>[<span class="ruby-value str">'authenticated'</span>] = <span class="ruby-keyword kw">false</span>
369:
370: <span class="ruby-identifier">goto</span> = <span class="ruby-ivar">@options</span>[<span class="ruby-identifier">:login_fail</span>] <span class="ruby-keyword kw">if</span> <span class="ruby-ivar">@options</span>.<span class="ruby-identifier">key?</span> <span class="ruby-identifier">:login_fail</span>
371: <span class="ruby-identifier">body</span> <span class="ruby-operator">&lt;&lt;</span> <span class="ruby-value str">&quot;Authentication cancelled.\n&quot;</span>
372: <span class="ruby-keyword kw">when</span> <span class="ruby-operator">::</span><span class="ruby-constant">OpenID</span><span class="ruby-operator">::</span><span class="ruby-constant">Consumer</span><span class="ruby-operator">::</span><span class="ruby-constant">SETUP_NEEDED</span>
373: <span class="ruby-identifier">session</span>[<span class="ruby-identifier">:setup_needed</span>] = <span class="ruby-keyword kw">true</span>
374: <span class="ruby-keyword kw">unless</span> <span class="ruby-identifier">o_id</span> = <span class="ruby-identifier">session</span>[<span class="ruby-identifier">:openid_param</span>]
375: <span class="ruby-identifier">raise</span>(<span class="ruby-value str">'Required values missing.'</span>)
376: <span class="ruby-keyword kw">end</span>
377:
378: <span class="ruby-identifier">goto</span> = <span class="ruby-identifier">req</span>.<span class="ruby-identifier">script_name</span><span class="ruby-operator">+</span>
379: <span class="ruby-value str">'?'</span><span class="ruby-operator">+</span><span class="ruby-ivar">@options</span>[<span class="ruby-identifier">:openid_param</span>]<span class="ruby-operator">+</span>
380: <span class="ruby-value str">'='</span><span class="ruby-operator">+</span><span class="ruby-identifier">o_id</span>
381: <span class="ruby-identifier">body</span> <span class="ruby-operator">&lt;&lt;</span> <span class="ruby-value str">&quot;Reauthentication required.\n&quot;</span>
382: <span class="ruby-keyword kw">end</span>
383: <span class="ruby-identifier">body</span> <span class="ruby-operator">&lt;&lt;</span> <span class="ruby-identifier">oid</span>.<span class="ruby-identifier">message</span> <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">oid</span>.<span class="ruby-identifier">message</span>
384: [ <span class="ruby-value">303</span>, {<span class="ruby-value str">'Location'</span>=<span class="ruby-operator">&gt;</span><span class="ruby-identifier">goto</span>}, <span class="ruby-identifier">body</span>]
385: <span class="ruby-keyword kw">end</span>
</pre>
</div>
</div>
</div>
</div>
</div>
<div id="validator-badges">
<p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
</div>
</body>
</html>