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