~ntt-pf-lab/nova/monkey_patch_notification

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/doc/web/howto/web-in-60/http-auth.html

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?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 lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
2
  <head>
 
3
<title>Twisted Documentation: HTTP Authentication</title>
 
4
<link href="../stylesheet.css" rel="stylesheet" type="text/css"/>
 
5
  </head>
 
6
 
 
7
  <body bgcolor="white">
 
8
    <h1 class="title">HTTP Authentication</h1>
 
9
    <div class="toc"><ol/></div>
 
10
    <div class="content">
 
11
<span/>
 
12
 
 
13
<p>Many of the previous examples have looked at how to serve content by using
 
14
existing resource classes or implementing new ones. In this example we'll use
 
15
Twisted Web's basic or digest HTTP authentication to control access to these
 
16
resources.</p>
 
17
 
 
18
<p><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.web.guard.html" title="twisted.web.guard">guard</a></code>, the Twisted Web
 
19
module which provides most of the APIs that will be used in this example, helps
 
20
you to
 
21
add <a href="http://en.wikipedia.org/wiki/Authentication" shape="rect">authentication</a>
 
22
and <a href="http://en.wikipedia.org/wiki/Authorization" shape="rect">authorization</a> to a
 
23
resource hierarchy. It does this by providing a resource which implements
 
24
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.web.resource.Resource.getChild.html" title="twisted.web.resource.Resource.getChild">getChild</a></code> to return
 
25
a <a href="dynamic-dispatch.html" shape="rect">dynamically selected resource</a>. The
 
26
selection is based on the authentication headers in the request. If those
 
27
headers indicate that the request is made on behalf of Alice, then Alice's
 
28
resource will be returned. If they indicate that it was made on behalf of Bob,
 
29
his will be returned. If the headers contain invalid credentials, an error
 
30
resource is returned. Whatever happens, once this resource is returned, URL
 
31
traversal continues as normal from that resource.</p>
 
32
 
 
33
<p>The resource that implements this is <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.web.guard.HTTPAuthSessionWrapper.html" title="twisted.web.guard.HTTPAuthSessionWrapper">HTTPAuthSessionWrapper</a></code>, though it is directly
 
34
responsible for very little of the process. It will extract headers from the
 
35
request and hand them off to a credentials factory to parse them according to
 
36
the appropriate standards (eg <a href="http://tools.ietf.org/html/rfc2617" shape="rect">HTTP
 
37
Authentication: Basic and Digest Access Authentication</a>) and then hand the
 
38
resulting credentials object off to a <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.portal.Portal.html" title="twisted.cred.portal.Portal">Portal</a></code>, the core
 
39
of <a href="../../../core/howto/cred.html" shape="rect">Twisted Cred</a>, a system for
 
40
uniform handling of authentication and authorization. We won't discuss Twisted
 
41
Cred in much depth here. To make use of it with Twisted Web, the only thing you
 
42
really need to know is how to implement an <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.portal.IRealm.html" title="twisted.cred.portal.IRealm">IRealm</a></code>.</p>
 
43
 
 
44
<p>You need to implement a realm because the realm is the object that actually
 
45
decides which resources are used for which users. This can be as complex or as
 
46
simple as it suitable for your application. For this example we'll keep it very
 
47
simple: each user will have a resource which is a static file listing of the
 
48
<code>public_html</code> directory in their UNIX home directory. First, we need
 
49
to import <code>implements</code> from <code>zope.interface</code> and <code>IRealm</code> from
 
50
<code>twisted.cred.portal</code>. Together these will let me mark this class as
 
51
a realm (this is mostly - but not entirely - a documentation thing). We'll also
 
52
need <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.web.static.File.html" title="twisted.web.static.File">File</a></code> for the actual
 
53
implementation later.</p>
 
54
 
 
55
<pre class="python"><p class="py-linenumber">1
 
56
2
 
57
3
 
58
4
 
59
5
 
60
6
 
61
7
 
62
</p><span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span>
 
63
 
 
64
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span>.<span class="py-src-variable">portal</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">IRealm</span>
 
65
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">static</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">File</span>
 
66
 
 
67
<span class="py-src-keyword">class</span> <span class="py-src-identifier">PublicHTMLRealm</span>(<span class="py-src-parameter">object</span>):
 
68
    <span class="py-src-variable">implements</span>(<span class="py-src-variable">IRealm</span>)
 
69
</pre>
 
70
 
 
71
<p>A realm only needs to implement one method: <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.portal.IRealm.requestAvatar.html" title="twisted.cred.portal.IRealm.requestAvatar">requestAvatar</a></code>. This method is called
 
72
after any successful authentication attempt (ie, Alice supplied the right
 
73
password). Its job is to return the <i>avatar</i> for the user who succeeded in
 
74
authenticating. An <i>avatar</i> is just an object that represents a user. In
 
75
this case, it will be a <code>File</code>. In general, with <code>Guard</code>,
 
76
the avatar must be a resource of some sort.</p>
 
77
 
 
78
<pre class="python"><p class="py-linenumber">1
 
79
2
 
80
3
 
81
4
 
82
</p><span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarId</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
 
83
    <span class="py-src-keyword">if</span> <span class="py-src-variable">IResource</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>:
 
84
        <span class="py-src-keyword">return</span> (<span class="py-src-variable">IResource</span>, <span class="py-src-variable">File</span>(<span class="py-src-string">&quot;/home/%s/public_html&quot;</span> % (<span class="py-src-variable">avatarId</span>,)), <span class="py-src-keyword">lambda</span>: <span class="py-src-variable">None</span>)
 
85
    <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span>()
 
86
</pre>
 
87
 
 
88
<p>A few notes on this method:</p>
 
89
<ul>
 
90
  <li>The <code>avatarId</code> parameter is essentially the username. It's the
 
91
    job of some other code to extract the username from the request headers and
 
92
    make sure it gets passed here.</li>
 
93
  <li>The <code>mind</code> is always <code>None</code> when writing a realm to
 
94
    be used with <code>Guard</code>. You can ignore it until you want to write a
 
95
    realm for something else.</li>
 
96
  <li><code>Guard</code> is always
 
97
    passed <code class="twisted.web.resource">IResource</code> as
 
98
    the <code>interfaces</code> parameter. If <code>interfaces</code> only
 
99
    contains interfaces your code doesn't understand,
 
100
    raising <code>NotImplementedError</code> is the thing to do, as
 
101
    above. You'll only need to worry about getting a different interface when
 
102
    you write a realm for something other than <code>Guard</code>.</li>
 
103
  <li>If you want to track when a user logs out, that's what the last element of
 
104
    the returned tuple is for. It will be called when this avatar logs
 
105
    out. <code>lambda: None</code> is the idiomatic no-op logout function.</li>
 
106
  <li>Notice that the path handling code in this example is written very
 
107
    poorly. This example may be vulnerable to certain unintentional information
 
108
    disclosure attacks. This sort of problem is exactly the
 
109
    reason <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.python.filepath.FilePath.html" title="twisted.python.filepath.FilePath">FilePath</a></code>
 
110
    exists. However, that's an example for another day...</li>
 
111
</ul>
 
112
 
 
113
<p>We're almost ready to set up the resource for this example. To create an
 
114
<code>HTTPAuthSessionWrapper</code>, though, we need two things. First, a
 
115
portal, which requires the realm above, plus at least one credentials
 
116
checker:</p>
 
117
 
 
118
<pre class="python"><p class="py-linenumber">1
 
119
2
 
120
3
 
121
4
 
122
</p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span>.<span class="py-src-variable">portal</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Portal</span>
 
123
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span>.<span class="py-src-variable">checkers</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">FilePasswordDB</span>
 
124
 
 
125
<span class="py-src-variable">portal</span> = <span class="py-src-variable">Portal</span>(<span class="py-src-variable">PublicHTMLRealm</span>(), [<span class="py-src-variable">FilePasswordDB</span>(<span class="py-src-string">'httpd.password'</span>)])
 
126
</pre>
 
127
 
 
128
<p><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.checkers.FilePasswordDB.html" title="twisted.cred.checkers.FilePasswordDB">FilePasswordDB</a></code> is the
 
129
credentials checker. It knows how to read <code>passwd(5)</code>-style (loosely)
 
130
files to check credentials against. It is responsible for the authentication
 
131
work after <code>HTTPAuthSessionWrapper</code> extracts the credentials from the
 
132
request.</p>
 
133
 
 
134
<p>Next we need either <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.web.guard.BasicCredentialFactory.html" title="twisted.web.guard.BasicCredentialFactory">BasicCredentialFactory</a></code> or
 
135
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.web.guard.DigestCredentialFactory.html" title="twisted.web.guard.DigestCredentialFactory">DigestCredentialFactory</a></code>. The
 
136
former knows how to challenge HTTP clients to do basic authentication; the
 
137
latter, digest authentication. We'll use digest here:</p>
 
138
 
 
139
<pre class="python"><p class="py-linenumber">1
 
140
2
 
141
3
 
142
</p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">guard</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">DigestCredentialFactory</span>
 
143
 
 
144
<span class="py-src-variable">credentialFactory</span> = <span class="py-src-variable">DigestCredentialFactory</span>(<span class="py-src-string">&quot;md5&quot;</span>, <span class="py-src-string">&quot;example.org&quot;</span>)
 
145
</pre>
 
146
 
 
147
<p>The two parameters to this constructor are the hash algorithm and the HTTP
 
148
authentication realm which will be used. The only other valid hash algorithm is
 
149
&quot;sha&quot; (but be careful, MD5 is more widely supported than SHA). The HTTP
 
150
authentication realm is mostly just a string that is presented to the user to
 
151
let them know why they're authenticating (you can read more about this in the
 
152
<a href="http://tools.ietf.org/html/rfc2617" shape="rect">RFC</a>).</p>
 
153
 
 
154
<p>With those things created, we can finally
 
155
instantiate <code>HTTPAuthSessionWrapper</code>:</p>
 
156
 
 
157
<pre class="python"><p class="py-linenumber">1
 
158
2
 
159
3
 
160
</p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">guard</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">HTTPAuthSessionWrapper</span>
 
161
 
 
162
<span class="py-src-variable">resource</span> = <span class="py-src-variable">HTTPAuthSessionWrapper</span>(<span class="py-src-variable">portal</span>, [<span class="py-src-variable">credentialFactory</span>])
 
163
</pre>
 
164
 
 
165
<p>There's just one last thing that needs to be done
 
166
here. When <a href="rpy-scripts.html" shape="rect">rpy scripts</a> were introduced, it was
 
167
mentioned that they are evaluated in an unusual context. This is the first
 
168
example that actually needs to take this into account. It so happens that
 
169
<code>DigestCredentialFactory</code> instances are stateful. Authentication will
 
170
only succeed if the same instance is used to both generate challenges and
 
171
examine the responses to those challenges. However, the normal mode of operation
 
172
for an rpy script is for it to be re-executed for every request. This leads to a
 
173
new
 
174
<code>DigestCredentialFactory</code> being created for every request, preventing
 
175
any authentication attempt from ever succeeding.</p>
 
176
 
 
177
<p>There are two ways to deal with this. First, and the better of the two ways,
 
178
we could move almost all of the code into a real Python module, including the
 
179
code that instantiates the <code>DigestCredentialFactory</code>. This would
 
180
ensure that the same instance was used for every request. Second, and the easier
 
181
of the two ways, we could add a call to <code>cache()</code> to the beginning of
 
182
the rpy script:</p>
 
183
 
 
184
<pre class="python"><p class="py-linenumber">1
 
185
</p><span class="py-src-variable">cache</span>()
 
186
</pre>
 
187
 
 
188
<p><code>cache</code> is part of the globals of any rpy script, so you don't
 
189
need to import it (it's okay to be cringing at this
 
190
point). Calling <code>cache</code> makes Twisted re-use the result of the first
 
191
evaluation of the rpy script for subsequent requests too - just what we want in
 
192
this case.</p>
 
193
 
 
194
<p>Here's the complete example (with imports re-arranged to the more
 
195
conventional style):</p>
 
196
 
 
197
<pre class="python"><p class="py-linenumber"> 1
 
198
 2
 
199
 3
 
200
 4
 
201
 5
 
202
 6
 
203
 7
 
204
 8
 
205
 9
 
206
10
 
207
11
 
208
12
 
209
13
 
210
14
 
211
15
 
212
16
 
213
17
 
214
18
 
215
19
 
216
20
 
217
21
 
218
22
 
219
</p><span class="py-src-variable">cache</span>()
 
220
 
 
221
<span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span>
 
222
 
 
223
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span>.<span class="py-src-variable">portal</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">IRealm</span>, <span class="py-src-variable">Portal</span>
 
224
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span>.<span class="py-src-variable">checkers</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">FilePasswordDB</span>
 
225
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">static</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">File</span>
 
226
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">resource</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">IResource</span>
 
227
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">guard</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">HTTPAuthSessionWrapper</span>, <span class="py-src-variable">DigestCredentialFactory</span>
 
228
 
 
229
<span class="py-src-keyword">class</span> <span class="py-src-identifier">PublicHTMLRealm</span>(<span class="py-src-parameter">object</span>):
 
230
    <span class="py-src-variable">implements</span>(<span class="py-src-variable">IRealm</span>)
 
231
 
 
232
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarId</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
 
233
        <span class="py-src-keyword">if</span> <span class="py-src-variable">IResource</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>:
 
234
            <span class="py-src-keyword">return</span> (<span class="py-src-variable">IResource</span>, <span class="py-src-variable">File</span>(<span class="py-src-string">&quot;/home/%s/public_html&quot;</span> % (<span class="py-src-variable">avatarId</span>,)), <span class="py-src-keyword">lambda</span>: <span class="py-src-variable">None</span>)
 
235
        <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span>()
 
236
 
 
237
<span class="py-src-variable">portal</span> = <span class="py-src-variable">Portal</span>(<span class="py-src-variable">PublicHTMLRealm</span>(), [<span class="py-src-variable">FilePasswordDB</span>(<span class="py-src-string">'httpd.password'</span>)])
 
238
 
 
239
<span class="py-src-variable">credentialFactory</span> = <span class="py-src-variable">DigestCredentialFactory</span>(<span class="py-src-string">&quot;md5&quot;</span>, <span class="py-src-string">&quot;localhost:8080&quot;</span>)
 
240
<span class="py-src-variable">resource</span> = <span class="py-src-variable">HTTPAuthSessionWrapper</span>(<span class="py-src-variable">portal</span>, [<span class="py-src-variable">credentialFactory</span>])
 
241
</pre>
 
242
 
 
243
<p>And voila, a password-protected per-user Twisted Web server.</p>
 
244
 
 
245
</div>
 
246
 
 
247
    <p><a href="../index.html">Index</a></p>
 
248
    <span class="version">Version: 10.0.0</span>
 
249
  </body>
 
250
</html>
 
 
b'\\ No newline at end of file'