Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 1240 lines (928 sloc) 52.017 kb
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1 =============================================
2 Chapter 14: Sessions, Users, and Registration
3 =============================================
4
5 It's time for a confession: we've been deliberately ignoring an important
6 aspect of Web development prior to this point. So far, we've thought of the
7 traffic visiting our sites as some faceless, anonymous mass hurtling itself
8 against our carefully designed pages.
9
10 This isn't true, of course. The browsers hitting our sites have real humans
11 behind them (most of the time, at least). That's a big thing to ignore: the
12 Internet is at its best when it serves to connect *people*, not machines. If
13 we're going to develop truly compelling sites, eventually we're going to have
14 to deal with the bodies behind the browsers.
15
16 Unfortunately, it's not all that easy. HTTP is designed to be *stateless*--
17 that is, each and every request happens in a vacuum. There's no persistence
18 between one request and the next, and we can't count on any aspects of a
19 request (IP address, user agent, etc.) to consistently indicate successive
20 requests from the same person.
21
22 In this chapter you'll learn how to handle this lack of state. We'll start at
23 the lowest level (*cookies*), and work up to the high-level tools for handling
24 sessions, users and registration.
25
26 Cookies
27 =======
28
29 Browser developers long ago recognized that HTTP's statelessness poses a huge
30 problem for Web developers, and thus *cookies* were born. A cookie is a
31 small piece of information that browsers store on behalf of Web servers. Every
32 time a browser requests a page from a certain server, it gives back the cookie
33 that it initially received.
34
35 Let's take a look how this might work. When you open your browser and type in
36 ``google.com``, your browser sends an HTTP request to Google that starts
37 something like this::
38
39 GET / HTTP/1.1
40 Host: google.com
41 ...
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
42
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
43 When Google replies, the HTTP response looks something like the following::
44
45 HTTP/1.1 200 OK
46 Content-Type: text/html
47 Set-Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671;
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
48 expires=Sun, 17-Jan-2038 19:14:07 GMT;
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
49 path=/; domain=.google.com
50 Server: GWS/2.1
51 ...
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
52
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
53 Notice the ``Set-Cookie`` header. Your browser will store that cookie value
54 (``PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671``) and serve it back
55 to Google every time you access the site. So the next time you access Google,
56 your browser is going to send a request like this::
57
58 GET / HTTP/1.1
59 Host: google.com
60 Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671
61 ...
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
62
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
63 Google then can use that ``Cookie`` value to know that you're the same person
64 who accessed the site earlier. This value might, for example, be a key into a
65 database that stores user information. Google could (and does) use it to
66 display your account's username on the page.
67
68 Getting and Setting Cookies
69 ---------------------------
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
70
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
71 When dealing with persistence in Django, most of the time you'll want to use the
72 higher-level session and/or user frameworks discussed a little later in this
73 chapter. However, first look at how to read and write cookies at a low
74 level. This should help you understand how the rest of the tools discussed in
75 the chapter actually work, and it will come in handy if you ever need to play
76 with cookies directly.
77
78 Reading cookies that are already set is simple. Every ``HttpRequest``
79 object has a ``COOKIES`` object that acts like a dictionary; you can use it to
80 read any cookies that the browser has sent to the view::
81
82 def show_color(request):
83 if "favorite_color" in request.COOKIES:
84 return HttpResponse("Your favorite color is %s" % \
85 request.COOKIES["favorite_color"])
86 else:
87 return HttpResponse("You don't have a favorite color.")
88
89 Writing cookies is slightly more complicated. You need to use the
90 ``set_cookie()`` method on an ``HttpResponse`` object. Here's an example that
91 sets the ``favorite_color`` cookie based on a ``GET`` parameter::
92
93 def set_color(request):
94 if "favorite_color" in request.GET:
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
95
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
96 # Create an HttpResponse object...
97 response = HttpResponse("Your favorite color is now %s" % \
98 request.GET["favorite_color"])
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
99
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
100 # ... and set a cookie on the response
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
101 response.set_cookie("favorite_color",
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
102 request.GET["favorite_color"])
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
103
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
104 return response
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
105
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
106 else:
107 return HttpResponse("You didn't give a favorite color.")
108
109 .. SL Tested ok
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
110
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
111 You can also pass a number of optional arguments to ``response.set_cookie()``
112 that control aspects of the cookie, as shown in Table 14-1.
113
114 .. table:: Table 14-1: Cookie options
115
116 ============== ========== ==============================================
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
117 Parameter Default Description
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
118 ============== ========== ==============================================
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
119 ``max_age`` ``None`` Age (in seconds) that the cookie should last.
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
120 If this parameter is ``None``, the cookie will
121 last only until the browser is closed.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
122
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
123 ``expires`` ``None`` The actual date/time when the cookie should
124 expire. It needs to be in the format ``"Wdy,
125 DD-Mth-YY HH:MM:SS GMT"``. If given, this
126 parameter overrides the ``max_age`` parameter.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
127
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
128 ``path`` ``"/"`` The path prefix that this cookie is valid for.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
129 Browsers will only pass the cookie back to
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
130 pages below this path prefix, so you can use
131 this to prevent cookies from being sent to
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
132 other sections of your site.
133
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
134 This is especially useful when you don't
135 control the top level of your site's domain.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
136
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
137 ``domain`` ``None`` The domain that this cookie is valid for. You
138 can use this parameter to set a cross-domain
139 cookie. For example, ``domain=".example.com"``
140 will set a cookie that is readable by the
141 domains ``www.example.com``,
142 ``www2.example.com``, and
143 ``an.other.sub.domain.example.com``.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
144
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
145 If this parameter is set to ``None``, a cookie
146 will only be readable by the domain that set it.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
147
148 ``secure`` ``False`` If set to ``True``, this parameter instructs the
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
149 browser to only return this cookie to pages
150 accessed over HTTPS.
151 ============== ========== ==============================================
152
153 The Mixed Blessing of Cookies
154 -----------------------------
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
155
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
156 You might notice a number of potential problems with the way cookies work.
157 Let's look at some of the more important ones:
158
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
159 * Storage of cookies is voluntary; a client does not have to accept or
160 store cookies. In fact, all browsers enable users to control the policy
161 for accepting cookies. If you want to see just how vital cookies are to
162 the Web, try turning on your browser's "prompt to accept every cookie"
163 option.
164
165 Despite their nearly universal use, cookies are still the definition of
166 unreliability. This means that developers should check that a user
167 actually accepts cookies before relying on them.
168
169 * Cookies (especially those not sent over HTTPS) are not secure. Because
170 HTTP data is sent in cleartext, cookies are extremely vulnerable to
171 snooping attacks. That is, an attacker snooping on the wire can intercept
172 a cookie and read it. This means you should never store sensitive
173 information in a cookie.
174
175 There's an even more insidious attack, known as a *man-in-the-middle*
176 attack, wherein an attacker intercepts a cookie and uses it to pose as
177 another user. Chapter 20 discusses attacks of this nature in depth, as
178 well as ways to prevent it.
179
180 * Cookies aren't even secure from their intended recipients. Most browsers
181 provide easy ways to edit the content of individual cookies, and
182 resourceful users can always use tools like mechanize
183 (http://wwwsearch.sourceforge.net/mechanize/) to construct HTTP requests
184 by hand.
185
186 So you can't store data in cookies that might be sensitive to tampering.
187 The canonical mistake in this scenario is storing something like
188 ``IsLoggedIn=1`` in a cookie when a user logs in. You'd be amazed at the
189 number of sites that make mistakes of this nature; it takes only a
190 second to fool these sites' "security" systems.
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
191
192 Django's Session Framework
193 ==========================
194
195 With all of these limitations and potential security holes, it's obvious that
196 cookies and persistent sessions are examples of those "pain points" in Web
197 development. Of course, Django's goal is to be an effective painkiller, so
198 it comes with a session framework designed to smooth over these
199 difficulties for you.
200
201 This session framework lets you store and retrieve arbitrary data on a
202 per-site visitor basis. It stores data on the server side and abstracts the
203 sending and receiving of cookies. Cookies use only a hashed session ID -- not
204 the data itself -- thus protecting you from most of the common cookie
205 problems.
206
207 Let's look at how to enable sessions and use them in views.
208
209 Enabling Sessions
210 -----------------
211
212 Sessions are implemented via a piece of middleware (see Chapter 17) and a Django
213 model. To enable sessions, you'll need to follow these steps:
214
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
215 #. Edit your ``MIDDLEWARE_CLASSES`` setting and make sure
216 ``MIDDLEWARE_CLASSES`` contains
217 ``'django.contrib.sessions.middleware.SessionMiddleware'``.
218
219 #. Make sure ``'django.contrib.sessions'`` is in your ``INSTALLED_APPS``
220 setting (and run ``manage.py syncdb`` if you have to add it).
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
221
222 The default skeleton settings created by ``startproject`` have both of these
223 bits already installed, so unless you've removed them, you probably don't have
224 to change anything to get sessions to work.
225
226 If you don't want to use sessions, you might want to remove the
227 ``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and
228 ``'django.contrib.sessions'`` from your ``INSTALLED_APPS``. It will save
229 you only a small amount of overhead, but every little bit counts.
230
231 Using Sessions in Views
232 -----------------------
233
234 When ``SessionMiddleware`` is activated, each ``HttpRequest`` object -- the
235 first argument to any Django view function -- will have a ``session``
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
236 attribute, which is a dictionary-like object. You can read it and write to it
237 in the same way you'd use a normal dictionary. For example, in a view
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
238 you could do stuff like this::
239
240 # Set a session value:
241 request.session["fav_color"] = "blue"
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
242
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
243 # Get a session value -- this could be called in a different view,
244 # or many requests later (or both):
245 fav_color = request.session["fav_color"]
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
246
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
247 # Clear an item from the session:
248 del request.session["fav_color"]
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
249
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
250 # Check if the session has a given key:
251 if "fav_color" in request.session:
252 ...
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
253
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
254 You can also use other dictionary methods like ``keys()`` and ``items()`` on
255 ``request.session``.
256
257 There are a couple of simple rules for using Django's sessions effectively:
258
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
259 * Use normal Python strings as dictionary keys on ``request.session`` (as
260 opposed to integers, objects, etc.).
261
262 * Session dictionary keys that begin with an underscore are reserved for
263 internal use by Django. In practice, the framework uses only a small
264 number of underscore-prefixed session variables, but unless you know what
265 they all are (and you are willing to keep up with any changes in Django
266 itself), staying away from underscore prefixes will keep Django from
267 interfering with your application.
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
268
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
269 For example, don't use a session key called ``_fav_color``, like
270 this::
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
271
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
272 request.session['_fav_color'] = 'blue' # Don't do this!
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
273
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
274 * Don't replace ``request.session`` with a new object, and don't access or
275 set its attributes. Use it like a Python dictionary. Examples::
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
276
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
277 request.session = some_other_object # Don't do this!
278
279 request.session.foo = 'bar' # Don't do this!
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
280
281 Let's take a look at a few quick examples. This simplistic view sets a
282 ``has_commented`` variable to ``True`` after a user posts a comment. It's a
283 simple (if not particularly secure) way of preventing a user from
284 posting more than one comment::
285
286 def post_comment(request):
287 if request.method != 'POST':
288 raise Http404('Only POSTs are allowed')
289
290 if 'comment' not in request.POST:
291 raise Http404('Comment not submitted')
292
293 if request.session.get('has_commented', False):
294 return HttpResponse("You've already commented.")
295
296 c = comments.Comment(comment=request.POST['comment'])
297 c.save()
298 request.session['has_commented'] = True
299 return HttpResponse('Thanks for your comment!')
300
301 This simplistic view logs in a "member" of the site::
302
303 def login(request):
304 if request.method != 'POST':
305 raise Http404('Only POSTs are allowed')
306 try:
307 m = Member.objects.get(username=request.POST['username'])
308 if m.password == request.POST['password']:
309 request.session['member_id'] = m.id
310 return HttpResponseRedirect('/you-are-logged-in/')
311 except Member.DoesNotExist:
312 return HttpResponse("Your username and password didn't match.")
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
313
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
314 And this one logs out a member who has been logged in via ``login()`` above::
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
315
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
316 def logout(request):
317 try:
318 del request.session['member_id']
319 except KeyError:
320 pass
321 return HttpResponse("You're logged out.")
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
322
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
323 .. note::
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
324
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
325 In practice, this is a lousy way of logging users in. The authentication
326 framework discussed shortly handles this task for you in a much more robust
327 and useful manner. These examples are deliberately simplistic so that you
328 can easily see what's going on.
329
330 Setting Test Cookies
331 --------------------
332
333 As mentioned above, you can't rely on every browser accepting cookies. So, as
334 a convenience, Django provides an easy way to test whether a user's browser
335 accepts cookies. Just call ``request.session.set_test_cookie()`` in a view, and
336 check ``request.session.test_cookie_worked()`` in a subsequent view -- not in
337 the same view call.
338
339 This awkward split between ``set_test_cookie()`` and ``test_cookie_worked()``
340 is necessary due to the way cookies work. When you set a cookie, you can't
341 actually tell whether a browser accepted it until the browser's next request.
342
343 It's good practice to use ``delete_test_cookie()`` to clean up after yourself.
344 Do this after you've verified that the test cookie worked.
345
346 Here's a typical usage example::
347
348 def login(request):
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
349
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
350 # If we submitted the form...
351 if request.method == 'POST':
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
352
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
353 # Check that the test cookie worked (we set it below):
354 if request.session.test_cookie_worked():
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
355
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
356 # The test cookie worked, so delete it.
357 request.session.delete_test_cookie()
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
358
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
359 # In practice, we'd need some logic to check username/password
360 # here, but since this is an example...
361 return HttpResponse("You're logged in.")
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
362
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
363 # The test cookie failed, so display an error message. If this
364 # were a real site, we'd want to display a friendlier message.
365 else:
366 return HttpResponse("Please enable cookies and try again.")
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
367
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
368 # If we didn't post, send the test cookie along with the login form.
369 request.session.set_test_cookie()
a4724d5 @RaphaelKimmig replace render_to_response with render
RaphaelKimmig authored
370 return render(request, 'foo/login_form.html')
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
371
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
372 .. note::
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
373
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
374 Again, the built-in authentication functions handle this check for you.
375
376 Using Sessions Outside of Views
377 -------------------------------
378
379 Internally, each session is just a normal Django model defined in
380 ``django.contrib.sessions.models``. Each session is identified by a more-or-less
381 random 32-character hash stored in a cookie. Because it's a normal model, you
382 can access sessions using the normal Django database API::
383
384 >>> from django.contrib.sessions.models import Session
385 >>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead')
386 >>> s.expire_date
387 datetime.datetime(2005, 8, 20, 13, 35, 12)
388
389 You'll need to call ``get_decoded()`` to get the actual session data. This is
390 necessary because the dictionary is stored in an encoded format::
391
392 >>> s.session_data
393 'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
394 >>> s.get_decoded()
395 {'user_id': 42}
396
397 When Sessions Are Saved
398 -----------------------
399
400 By default, Django only saves to the database if the session has been modified
401 -- that is, if any of its dictionary values have been assigned or deleted::
402
403 # Session is modified.
404 request.session['foo'] = 'bar'
405
406 # Session is modified.
407 del request.session['foo']
408
409 # Session is modified.
410 request.session['foo'] = {}
411
412 # Gotcha: Session is NOT modified, because this alters
413 # request.session['foo'] instead of request.session.
414 request.session['foo']['bar'] = 'baz'
415
416 To change this default behavior, set ``SESSION_SAVE_EVERY_REQUEST``
417 to ``True``. If ``SESSION_SAVE_EVERY_REQUEST`` is ``True``, Django
418 will save the session to the database on every single request, even if it
419 wasn't changed.
420
421 Note that the session cookie is sent only when a session has been created or
422 modified. If ``SESSION_SAVE_EVERY_REQUEST`` is ``True``, the session cookie
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
423 will be sent on every request. Similarly, the ``expires`` part of a session
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
424 cookie is updated each time the session cookie is sent.
425
426 Browser-Length Sessions vs. Persistent Sessions
427 -----------------------------------------------
428
429 You might have noticed that the cookie Google sent us at the beginning of this
430 chapter contained ``expires=Sun, 17-Jan-2038 19:14:07 GMT;``. Cookies can
431 optionally contain an expiration date that advises the browser on when to
432 remove the cookie. If a cookie doesn't contain an expiration value, the browser
433 will expire it when the user closes his or her browser window. You can control
434 the session framework's behavior in this regard with the
435 ``SESSION_EXPIRE_AT_BROWSER_CLOSE`` setting.
436
437 By default, ``SESSION_EXPIRE_AT_BROWSER_CLOSE`` is set to ``False``, which means
438 session cookies will be stored in users' browsers for ``SESSION_COOKIE_AGE``
439 seconds (which defaults to two weeks, or 1,209,600 seconds). Use this if you
440 don't want people to have to log in every time they open a browser.
441
442 If ``SESSION_EXPIRE_AT_BROWSER_CLOSE`` is set to ``True``, Django will use
443 browser-length cookies.
444
445 Other Session Settings
446 ----------------------
447
448 Besides the settings already mentioned, a few other settings
449 influence how Django's session framework uses cookies, as shown in Table 14-2.
450
451 .. table:: Table 14-2. Settings that influence cookie behavior
452
453 ========================== ============================= ==============
454 Setting Description Default
455 ========================== ============================= ==============
456 ``SESSION_COOKIE_DOMAIN`` The domain to use for session ``None``
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
457 cookies. Set this to a string
458 such as ``".example.com"``
459 for cross-domain cookies, or
460 use ``None`` for a standard
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
461 cookie.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
462
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
463 ``SESSION_COOKIE_NAME`` The name of the cookie to use ``"sessionid"``
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
464 for sessions. This can be any
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
465 string.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
466
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
467 ``SESSION_COOKIE_SECURE`` Whether to use a "secure" ``False``
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
468 cookie for the session
469 cookie. If this is set to
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
470 ``True``, the cookie will be
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
471 marked as "secure," which
472 means that browsers will
473 ensure that the cookie is
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
474 only sent via HTTPS.
475 ========================== ============================= ==============
476
477 .. admonition:: Technical Details
478
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
479 For the curious, here are a few technical notes about the inner workings
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
480 of the session framework:
481
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
482 * The session dictionary accepts any Python object capable of being
483 "pickled." See the documentation for Python's built-in ``pickle``
484 module for information about how this works.
485
486 * Session data is stored in a database table named ``django_session``.
487
488 * Session data is fetched upon demand. If you never access
489 ``request.session``, Django won't hit that database table.
490
491 * Django only sends a cookie if it needs to. If you don't set any
492 session data, it won't send a session cookie (unless
493 ``SESSION_SAVE_EVERY_REQUEST`` is set to ``True``).
494
495 * The Django sessions framework is entirely, and solely, cookie based.
496 It does not fall back to putting session IDs in URLs as a last
497 resort, as some other tools (PHP, JSP) do.
498
499 This is an intentional design decision. Putting sessions in URLs
500 don't just make URLs ugly, but also make your site vulnerable to a
501 certain form of session ID theft via the ``Referer`` header.
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
502
503 If you're still curious, the source is pretty straightforward; look in
504 ``django.contrib.sessions`` for more details.
505
506 Users and Authentication
507 ========================
508
509 Sessions give us a way of persisting data through multiple browser requests;
510 the second part of the equation is using those sessions for user login. Of
511 course, we can't just trust that users are who they say they are, so we need
512 to authenticate them along the way.
513
514 Naturally, Django provides tools to handle this common task (and many others).
515 Django's user authentication system handles user accounts, groups, permissions,
516 and cookie-based user sessions. This system is often referred to as an
517 *auth/auth* (authentication and authorization) system. That name recognizes
518 that dealing with users is often a two-step process. We need to
519
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
520 #. Verify (*authenticate*) that a user is who he or she claims to be
521 (usually by checking a username and password against a database of users)
522
523 #. Verify that the user is *authorized* to perform some given operation
524 (usually by checking against a table of permissions)
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
525
526 Following these needs, Django's auth/auth system consists of a number of
527 parts:
528
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
529 * *Users*: People registered with your site
530
531 * *Permissions*: Binary (yes/no) flags designating whether a user may
532 perform a certain task
533
534 * *Groups*: A generic way of applying labels and permissions to more than
535 one user
536
537 * *Messages*: A simple way to queue and display system messages to users
538
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
539 If you've used the admin tool (discussed in Chapter 6), you've already seen many
540 of these tools, and if you've edited users or groups in the admin tool, you've
541 actually been editing data in the auth system's database tables.
542
543 Enabling Authentication Support
544 -------------------------------
545
546 Like the session tools, authentication support is bundled as a Django
547 application in ``django.contrib`` that needs to be installed. Also like the
548 session tools, it's also installed by default, but if you've removed it, you'll
549 need to follow these steps to install it:
550
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
551 #. Make sure the session framework is installed as described earlier in this
552 chapter. Keeping track of users obviously requires cookies, and thus
553 builds on the session framework.
554
555 #. Put ``'django.contrib.auth'`` in your ``INSTALLED_APPS`` setting and
556 run ``manage.py syncdb`` to install the appropriate database tables.
557
558 #. Make sure that
559 ``'django.contrib.auth.middleware.AuthenticationMiddleware'`` is in
560 your ``MIDDLEWARE_CLASSES`` setting -- *after* ``SessionMiddleware``.
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
561
562 With that installation out of the way, we're ready to deal with users in view
563 functions. The main interface you'll use to access users within a view is
564 ``request.user``; this is an object that represents the currently logged-in
565 user. If the user isn't logged in, this will instead be an ``AnonymousUser``
566 object (see below for more details).
567
568 You can easily tell if a user is logged in with the ``is_authenticated()``
569 method::
570
571 if request.user.is_authenticated():
572 # Do something for authenticated users.
573 else:
574 # Do something for anonymous users.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
575
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
576 Using Users
577 -----------
578
579 Once you have a ``User`` -- often from ``request.user``, but possibly through
580 one of the other methods discussed shortly -- you have a number of fields and
581 methods available on that object. ``AnonymousUser`` objects emulate *some* of
582 this interface, but not all of it, so you should always check
583 ``user.is_authenticated()`` before assuming you're dealing with a bona fide user
584 object. Tables 14-3 and 14-4 list the fields and methods, respectively, on ``User`` objects.
585
586 .. table:: Table 14-3. Fields on ``User`` Objects
587
588 ================== ======================================================
589 Field Description
590 ================== ======================================================
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
591 ``username`` Required; 30 characters or fewer. Alphanumeric
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
592 characters only (letters, digits, and underscores).
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
593
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
594 ``first_name`` Optional; 30 characters or fewer.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
595
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
596 ``last_name`` Optional; 30 characters or fewer.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
597
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
598 ``email`` Optional. E-mail address.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
599
600 ``password`` Required. A hash of, and metadata about, the password
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
601 (Django doesn't store the raw password). See the
602 "Passwords" section for more about this value.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
603
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
604 ``is_staff`` Boolean. Designates whether this user can access the
605 admin site.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
606
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
607 ``is_active`` Boolean. Designates whether this account can be used
608 to log in. Set this flag to ``False`` instead of
609 deleting accounts.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
610
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
611 ``is_superuser`` Boolean. Designates that this user has all permissions
612 without explicitly assigning them.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
613
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
614 ``last_login`` A datetime of the user's last login. This is set to the
615 current date/time by default.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
616
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
617 ``date_joined`` A datetime designating when the account was created.
618 This is set to the current date/time by default when the
619 account is created.
620 ================== ======================================================
621
622 .. table:: Table 14-4. Methods on ``User`` Objects
623
624 ================================ ==========================================
625 Method Description
626 ================================ ==========================================
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
627 ``is_authenticated()`` Always returns ``True`` for "real"
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
628 ``User`` objects. This is a way to tell if
629 the user has been authenticated. This does
630 not imply any permissions, and it doesn't
631 check if the user is active. It only
632 indicates that the user has sucessfully
633 authenticated.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
634
635 ``is_anonymous()`` Returns ``True`` only for
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
636 ``AnonymousUser`` objects (and ``False``
637 for "real" ``User`` objects). Generally,
638 you should prefer using
639 ``is_authenticated()`` to this method.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
640
641 ``get_full_name()`` Returns the ``first_name`` plus the
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
642 ``last_name``, with a space in between.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
643
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
644 ``set_password(passwd)`` Sets the user's password to the given
645 raw string, taking care of the password
646 hashing. This doesn't actually save the
647 ``User`` object.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
648
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
649 ``check_password(passwd)`` Returns ``True`` if the given raw
650 string is the correct password for the
651 user. This takes care of the password
652 hashing in making the comparison.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
653
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
654 ``get_group_permissions()`` Returns a list of permission strings that
655 the user has through the groups he or she
656 belongs to.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
657
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
658 ``get_all_permissions()`` Returns a list of permission strings that
659 the user has, both through group and user
660 permissions.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
661
662 ``has_perm(perm)`` Returns ``True`` if the user has the
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
663 specified permission, where ``perm`` is in
664 the format ``"package.codename"``. If the
665 user is inactive, this method will always
666 return ``False``.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
667
668 ``has_perms(perm_list)`` Returns ``True`` if the user has *all* of
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
669 the specified permissions. If the user is
670 inactive, this method will always return
671 ``False``.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
672
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
673 ``has_module_perms(app_label)`` Returns ``True`` if the user has
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
674 any permissions in the given ``app_label``.
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
675 If the user is inactive, this method will
676 always return ``False``.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
677
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
678 ``get_and_delete_messages()`` Returns a list of ``Message`` objects in
679 the user's queue and deletes the messages
680 from the queue.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
681
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
682 ``email_user(subj, msg)`` Sends an email to the user. This email
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
683 is sent from the ``DEFAULT_FROM_EMAIL``
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
684 setting. You can also pass a third
685 argument, ``from_email``, to override the
686 From address on the email.
687 ================================ ==========================================
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
688
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
689 Finally, ``User`` objects have two many-to-many fields: ``groups`` and
690 ``permissions``. ``User`` objects can access their related objects in the same
691 way as any other many-to-many field::
692
693 # Set a user's groups:
694 myuser.groups = group_list
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
695
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
696 # Add a user to some groups:
697 myuser.groups.add(group1, group2,...)
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
698
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
699 # Remove a user from some groups:
700 myuser.groups.remove(group1, group2,...)
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
701
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
702 # Remove a user from all groups:
703 myuser.groups.clear()
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
704
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
705 # Permissions work the same way
706 myuser.permissions = permission_list
707 myuser.permissions.add(permission1, permission2, ...)
708 myuser.permissions.remove(permission1, permission2, ...)
709 myuser.permissions.clear()
710
711 Logging In and Out
712 ------------------
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
713
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
714 Django provides built-in view functions for handling logging in and out (and a
715 few other nifty tricks), but before we get to those, let's take a look at how
716 to log users in and out "by hand." Django provides two functions to perform
717 these actions in ``django.contrib.auth``: ``authenticate()`` and ``login()``.
718
719 To authenticate a given username and password, use ``authenticate()``. It
720 takes two keyword arguments, ``username`` and ``password``, and it returns a
721 ``User`` object if the password is valid for the given username. If the
722 password is invalid, ``authenticate()`` returns ``None``::
723
724 >>> from django.contrib import auth
725 >>> user = auth.authenticate(username='john', password='secret')
726 >>> if user is not None:
727 ... print "Correct!"
728 ... else:
729 ... print "Invalid password."
730
731 ``authenticate()`` only verifies a user's credentials. To log in a user, use
732 ``login()``. It takes an ``HttpRequest`` object and a ``User`` object and saves
733 the user's ID in the session, using Django's session framework.
734
735 This example shows how you might use both ``authenticate()`` and ``login()``
736 within a view function::
737
738 from django.contrib import auth
739
740 def login_view(request):
741 username = request.POST.get('username', '')
742 password = request.POST.get('password', '')
743 user = auth.authenticate(username=username, password=password)
744 if user is not None and user.is_active:
745 # Correct password, and the user is marked "active"
746 auth.login(request, user)
747 # Redirect to a success page.
748 return HttpResponseRedirect("/account/loggedin/")
749 else:
750 # Show an error page
751 return HttpResponseRedirect("/account/invalid/")
752
753 To log out a user, use ``django.contrib.auth.logout()`` within your view. It
754 takes an ``HttpRequest`` object and has no return value::
755
756 from django.contrib import auth
757
758 def logout_view(request):
759 auth.logout(request)
760 # Redirect to a success page.
761 return HttpResponseRedirect("/account/loggedout/")
762
763 Note that ``auth.logout()`` doesn't throw any errors if the user wasn't logged
764 in.
765
766 In practice, you usually will not need to write your own login/logout functions;
767 the authentication system comes with a set of views for generically handling
768 logging in and out. The first step in using these authentication views is to
769 wire them up in your URLconf. You'll need to add this snippet::
770
771 from django.contrib.auth.views import login, logout
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
772
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
773 urlpatterns = patterns('',
774 # existing patterns here...
775 (r'^accounts/login/$', login),
776 (r'^accounts/logout/$', logout),
777 )
778
779 ``/accounts/login/`` and ``/accounts/logout/`` are the default URLs that
780 Django uses for these views.
781
782 By default, the ``login`` view renders a template at
783 ``registration/login.html`` (you can change this template name by passing an
784 extra view argument ,``template_name``). This form needs to contain a
785 ``username`` and a ``password`` field. A simple template might look like this::
786
787 {% extends "base.html" %}
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
788
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
789 {% block content %}
790
791 {% if form.errors %}
792 <p class="error">Sorry, that's not a valid username or password</p>
793 {% endif %}
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
794
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
795 <form action="" method="post">
796 <label for="username">User name:</label>
797 <input type="text" name="username" value="" id="username">
798 <label for="password">Password:</label>
799 <input type="password" name="password" value="" id="password">
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
800
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
801 <input type="submit" value="login" />
802 <input type="hidden" name="next" value="{{ next|escape }}" />
803 </form>
804
805 {% endblock %}
806
807 If the user successfully logs in, he or she will be redirected to
808 ``/accounts/profile/`` by default. You can override this by providing a hidden
809 field called ``next`` with the URL to redirect to after logging in. You can
810 also pass this value as a ``GET`` parameter to the login view and it will be
811 automatically added to the context as a variable called ``next`` that you can
812 insert into that hidden field.
813
814 The logout view works a little differently. By default it renders a template
815 at ``registration/logged_out.html`` (which usually contains a "You've
816 successfully logged out" message). However, you can call the view with an
817 extra argument, ``next_page``, which will instruct the view to redirect after
818 a logout.
819
820 Limiting Access to Logged-in Users
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
821 ----------------------------------
822
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
823 Of course, the reason we're going through all this trouble is so we can
824 limit access to parts of our site.
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
825
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
826 The simple, raw way to limit access to pages is to check
827 ``request.user.is_authenticated()`` and redirect to a login page::
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
828
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
829 from django.http import HttpResponseRedirect
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
830
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
831 def my_view(request):
832 if not request.user.is_authenticated():
833 return HttpResponseRedirect('/accounts/login/?next=%s' % request.path)
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
834 # ...
835
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
836 or perhaps display an error message::
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
837
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
838 def my_view(request):
839 if not request.user.is_authenticated():
a4724d5 @RaphaelKimmig replace render_to_response with render
RaphaelKimmig authored
840 return render(request, 'myapp/login_error.html')
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
841 # ...
842
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
843 As a shortcut, you can use the convenient ``login_required`` decorator::
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
844
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
845 from django.contrib.auth.decorators import login_required
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
846
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
847 @login_required
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
848 def my_view(request):
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
849 # ...
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
850
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
851 ``login_required`` does the following:
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
852
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
853 * If the user isn't logged in, redirect to ``/accounts/login/``, passing
854 the current URL path in the query string as ``next``, for example:
855 ``/accounts/login/?next=/polls/3/``.
856
857 * If the user is logged in, execute the view normally. The view code
858 can then assume that the user is logged in.
859
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
860 Limiting Access to Users Who Pass a Test
861 ----------------------------------------
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
862
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
863 Limiting access based on certain permissions or some other test, or providing
864 a different location for the login view works essentially the same way.
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
865
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
866 The raw way is to run your test on ``request.user`` in the view directly. For
867 example, this view checks to make sure the user is logged in and has the
868 permission ``polls.can_vote`` (more about how permissions
869 works follows)::
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
870
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
871 def vote(request):
872 if request.user.is_authenticated() and request.user.has_perm('polls.can_vote')):
873 # vote here
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
874 else:
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
875 return HttpResponse("You can't vote in this poll.")
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
876
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
877 Again, Django provides a shortcut called ``user_passes_test``. It
878 takes arguments and generates a specialized decorator for your particular
879 situation::
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
880
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
881 def user_can_vote(user):
882 return user.is_authenticated() and user.has_perm("polls.can_vote")
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
883
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
884 @user_passes_test(user_can_vote, login_url="/login/")
885 def vote(request):
886 # Code here can assume a logged-in user with the correct permission.
887 ...
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
888
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
889 ``user_passes_test`` takes one required argument: a callable that takes a
890 ``User`` object and returns ``True`` if the user is allowed to view the page.
891 Note that ``user_passes_test`` does not automatically check that the ``User``
892 is authenticated; you should do that yourself.
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
893
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
894 In this example we're also showing the second (optional) argument,
895 ``login_url``, which lets you specify the URL for your login page
896 (``/accounts/login/`` by default). If the user doesn't pass the test, then
897 the ``user_passes_test`` decorator will redirect the user to the ``login_url``.
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
898
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
899 Because it's a relatively common task to check whether a user has a particular
900 permission, Django provides a shortcut for that case: the
901 ``permission_required()`` decorator. Using this decorator, the earlier example
902 can be written as follows::
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
903
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
904 from django.contrib.auth.decorators import permission_required
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
905
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
906 @permission_required('polls.can_vote', login_url="/login/")
907 def vote(request):
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
908 # ...
909
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
910 Note that ``permission_required()`` also takes an optional ``login_url``
911 parameter, which also defaults to ``'/accounts/login/'``.
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
912
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
913 .. admonition:: Limiting Access to Generic Views
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
914
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
915 One of the most frequently asked questions on the Django users list deals
916 with limiting access to a generic view. To pull this off, you'll need to
917 write a thin wrapper around the view and point your URLconf to your wrapper
918 instead of the generic view itself::
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
919
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
920 from django.contrib.auth.decorators import login_required
921 from django.views.generic.date_based import object_detail
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
922
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
923 @login_required
924 def limited_object_detail(*args, **kwargs):
925 return object_detail(*args, **kwargs)
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
926
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
927 You can, of course, replace ``login_required`` with any of the other
928 limiting decorators.
929
930 Managing Users, Permissions, and Groups
931 ---------------------------------------
932
933 The easiest way by far to manage the auth system is through the admin interface.
934 Chapter 6 discusses how to use Django's admin site to edit users and
935 control their permissions and access, and most of the time you'll just use that
936 interface.
937
938 However, there are low-level APIs you can dive into when you need absolute
939 control, and we discuss these in the sections that follow.
940
941 Creating Users
942 ``````````````
943
944 Create users with the ``create_user`` helper function::
945
946 >>> from django.contrib.auth.models import User
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
947 >>> user = User.objects.create_user(username='john',
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
948 ... email='jlennon@beatles.com',
949 ... password='glass onion')
950
951 At this point, ``user`` is a ``User`` instance ready to be saved to the database
952 (``create_user()`` doesn't actually call ``save()`` itself). You can continue to
953 change its attributes before saving, too::
954
955 >>> user.is_staff = True
956 >>> user.save()
957
958 .. SL Tested ok
959
960 Changing Passwords
961 ``````````````````
962
963 You can change a password with ``set_password()``::
964
965 >>> user = User.objects.get(username='john')
966 >>> user.set_password('goo goo goo joob')
967 >>> user.save()
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
968
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
969 .. SL Tested ok
970
971 Don't set the ``password`` attribute directly unless you know what you're
972 doing. The password is actually stored as a *salted hash* and thus can't be
973 edited directly.
974
975 More formally, the ``password`` attribute of a ``User`` object is a string in
976 this format::
977
978 hashtype$salt$hash
979
980 That's a hash type, the salt, and the hash itself, separated by the dollar sign
981 ($) character.
982
983 ``hashtype`` is either ``sha1`` (default) or ``md5``, the algorithm used to
984 perform a one-way hash of the password. ``salt`` is a random string used to salt
985 the raw password to create the hash, for example::
986
987 sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4
988
989 The ``User.set_password()`` and ``User.check_password()`` functions handle the
990 setting and checking of these values behind the scenes.
991
992 .. admonition:: Salted hashes
993
994 A *hash* is a one-way cryptographic function -- that is, you can easily
995 compute the hash of a given value, but it's nearly impossible to take a
996 hash and reconstruct the original value.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
997
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
998 If we stored passwords as plain text, anyone who got their hands on the
999 password database would instantly know everyone's password. Storing
1000 passwords as hashes reduces the value of a compromised database.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
1001
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1002 However, an attacker with the password database could still run a *brute-
1003 force* attack, hashing millions of passwords and comparing those hashes
1004 against the stored values. This takes some time, but less than you might
1005 think.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
1006
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1007 Worse, there are publicly available *rainbow tables*, or databases of
1008 pre-computed hashes of millions of passwords. With a rainbow table, an
1009 experienced attacker could break most passwords in seconds.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
1010
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1011 Adding a *salt* -- basically an initial random value -- to the stored hash
1012 adds another layer of difficulty to breaking passwords. Because salts
1013 differ from password to password, they also prevent the use of a rainbow
1014 table, thus forcing attackers to fall back on a brute-force attack, itself
1015 made more difficult by the extra entropy added to the hash by the salt.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
1016
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1017 While salted hashes aren't absolutely the most secure way of storing
1018 passwords, they're a good middle ground between security and convenience.
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
1019
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1020 Handling Registration
1021 `````````````````````
1022
1023 We can use these low-level tools to create views that allow users to sign up
1024 for new accounts. Different developers implement registration differently, so
1025 Django leaves writing a registration view up to you. Luckily, it's pretty easy.
1026
1027 At its simplest, we could provide a small view that prompts for the required
1028 user information and creates those users. Django provides a built-in form you
1029 can use for this purpose, which we'll use in this example::
1030
1031 from django import forms
1032 from django.contrib.auth.forms import UserCreationForm
1033 from django.http import HttpResponseRedirect
a4724d5 @RaphaelKimmig replace render_to_response with render
RaphaelKimmig authored
1034 from django.shortcuts import render
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
1035
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1036 def register(request):
1037 if request.method == 'POST':
1038 form = UserCreationForm(request.POST)
1039 if form.is_valid():
1040 new_user = form.save()
1041 return HttpResponseRedirect("/books/")
1042 else:
1043 form = UserCreationForm()
a4724d5 @RaphaelKimmig replace render_to_response with render
RaphaelKimmig authored
1044 return render(request, "registration/register.html", {
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1045 'form': form,
1046 })
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1047
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1048 This form assumes a template named ``registration/register.html``. Here's an
1049 example of what that template might look like::
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1050
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1051 {% extends "base.html" %}
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
1052
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1053 {% block title %}Create an account{% endblock %}
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
1054
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1055 {% block content %}
1056 <h1>Create an account</h1>
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1057
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1058 <form action="" method="post">
1059 {{ form.as_p }}
1060 <input type="submit" value="Create the account">
1061 </form>
1062 {% endblock %}
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1063
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1064 .. SL Tested ok
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1065
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1066 Using Authentication Data in Templates
1067 --------------------------------------
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1068
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1069 The current logged-in user and his or her permissions are made available in the
a4724d5 @RaphaelKimmig replace render_to_response with render
RaphaelKimmig authored
1070 template context when you use the ``render()`` shortcut or explicitly use a
1071 ``RequestContext`` (see Chapter 9).
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1072
1073 .. note::
1074
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1075 Technically, these variables are only made available in the template
1076 context if you use ``RequestContext`` *and* your
1077 ``TEMPLATE_CONTEXT_PROCESSORS`` setting contains
1078 ``"django.core.context_processors.auth"``, which is the default. Again, see
1079 Chapter 9 for more information.
1080
1081 When using ``RequestContext``, the current user (either a ``User`` instance
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
1082 or an ``AnonymousUser`` instance) is stored in the template variable
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1083 ``{{ user }}``::
1084
1085 {% if user.is_authenticated %}
1086 <p>Welcome, {{ user.username }}. Thanks for logging in.</p>
1087 {% else %}
1088 <p>Welcome, new user. Please log in.</p>
1089 {% endif %}
1090
1091 This user's permissions are stored in the template variable ``{{ perms }}``.
1092 This is a template-friendly proxy to a couple of permission methods described
1093 shortly.
1094
1095 There are two ways you can use this ``perms`` object. You can use something like
1096 ``{% if perms.polls %}`` to check if the user has *any* permissions for some given
1097 application, or you can use something like ``{% if perms.polls.can_vote %}`` to
1098 check if the user has a specific permission.
1099
1100 Thus, you can check permissions in template ``{% if %}`` statements::
1101
1102 {% if perms.polls %}
1103 <p>You have permission to do something in the polls app.</p>
1104 {% if perms.polls.can_vote %}
1105 <p>You can vote!</p>
1106 {% endif %}
1107 {% else %}
1108 <p>You don't have permission to do anything in the polls app.</p>
1109 {% endif %}
1110
1111 Permissions, Groups and Messages
1112 ================================
1113
1114 There are a few other bits of the authentication framework that we've only dealt
1115 with in passing. We'll take a closer look at them in the following sections.
1116
1117 Permissions
1118 -----------
1119
1120 Permissions are a simple way to "mark" users and groups as being able to
1121 perform some action. They are usually used by the Django admin site, but you can
1122 easily use them in your own code.
1123
1124 The Django admin site uses permissions as follows:
1125
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
1126 * Access to view the "add" form, and add an object is limited to users with
1127 the *add* permission for that type of object.
1128
1129 * Access to view the change list, view the "change" form, and change an
1130 object is limited to users with the *change* permission for that type of
1131 object.
1132
1133 * Access to delete an object is limited to users with the *delete*
1134 permission for that type of object.
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1135
1136 Permissions are set globally per type of object, not per specific object
1137 instance. For example, it's possible to say "Mary may change news stories,"
1138 but permissions don't let you say "Mary may change news stories, but only
1139 the ones she created herself" or "Mary may only change news stories that have
1140 a certain status, publication date, or ID."
1141
1142 These three basic permissions -- add, change, and delete -- are automatically
1143 created for each Django model. Behind the scenes, these permissions are added
1144 to the ``auth_permission`` database table when you run ``manage.py syncdb``.
1145
1146 These permissions will be of the form ``"<app>.<action>_<object_name>"``. That
1147 is, if you have a ``polls`` application with a ``Choice`` model, you'll get
1148 permissions named ``"polls.add_choice"``, ``"polls.change_choice"``, and
1149 ``"polls.delete_choice"``.
1150
1151 Just like users, permissions are implemented in a Django model that lives in
1152 ``django.contrib.auth.models``. This means that you can use Django's database
1153 API to interact directly with permissions if you like.
1154
1155 Groups
1156 ------
1157
1158 Groups are a generic way of categorizing users so you can apply permissions,
1159 or some other label, to those users. A user can belong to any number of
1160 groups.
1161
1162 A user in a group automatically has the permissions granted to that group. For
1163 example, if the group ``Site editors`` has the permission
1164 ``can_edit_home_page``, any user in that group will have that permission.
1165
1166 Groups are also a convenient way to categorize users to give them some label, or
1167 extended functionality. For example, you could create a group ``'Special
1168 users'``, and you could write code that could, say, give those users access to a
1169 members-only portion of your site, or send them members-only e-mail messages.
1170
1171 Like users, the easiest way to manage groups is through the admin interface.
1172 However, groups are also just Django models that live in
1173 ``django.contrib.auth.models``, so once again you can always use Django's
1174 database APIs to deal with groups at a low level.
1175
1176 Messages
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1177 --------
1178
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1179 The message system is a lightweight way to queue messages for given users. A
1180 message is associated with a ``User``. There's no concept of expiration or
1181 timestamps.
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1182
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1183 Messages are used by the Django admin interface after successful actions. For
1184 example, when you create an object, you'll notice a "The object was created
1185 successfully" message at the top of the admin page.
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1186
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1187 You can use the same API to queue and display messages in your own application.
1188 The API is simple:
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1189
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
1190 * To create a new message, use
1191 ``user.message_set.create(message='message_text')``.
1192
1193 * To retrieve/delete messages, use ``user.get_and_delete_messages()``,
1194 which returns a list of ``Message`` objects in the user's queue (if any)
1195 and deletes the messages from the queue.
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1196
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1197 In this example view, the system saves a message for the user after creating a
1198 playlist::
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1199
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1200 def create_playlist(request, songs):
1201 # Create the playlist with the given songs.
1202 # ...
1203 request.user.message_set.create(
1204 message="Your playlist was added successfully."
1205 )
a4724d5 @RaphaelKimmig replace render_to_response with render
RaphaelKimmig authored
1206 return render(request, "playlists/create.html")
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1207
a4724d5 @RaphaelKimmig replace render_to_response with render
RaphaelKimmig authored
1208 When you use the ``render()`` shortcut or render a template with a
1209 ``RequestContext``, the current logged-in user and his or her messages are made
1210 available in the template context as the template variable ``{{ messages }}``.
1211 Here's an example of template code that displays messages::
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1212
1213 {% if messages %}
1214 <ul>
1215 {% for message in messages %}
1216 <li>{{ message }}</li>
1217 {% endfor %}
1218 </ul>
1219 {% endif %}
1220
1221 Note that ``RequestContext`` calls ``get_and_delete_messages`` behind the
1222 scenes, so any messages will be deleted even if you don't display them.
1223
1224 Finally, note that this messages framework only works with users in the user
1225 database. To send messages to anonymous users, use the session framework
1226 directly.
1227
1228 What's Next
1229 ===========
1230
1231 The session and authorization system is a lot to absorb. Most of the time,
1232 you won't need all the features described in this chapter, but when you need to
1233 allow complex interactions between users, it's good to have all that power
1234 available.
1235
1236 In the `next chapter`_, we'll take a look at Django's caching infrastructure,
1237 which is a convenient way to improve the performance of your application.
1238
494baa7 @belgianguy Removed trailing slash, rst to html
belgianguy authored
1239 .. _next chapter: chapter15.html
Something went wrong with that request. Please try again.