Django Su
It's always a sign of good design when adding a new feature turns out to be easy.
In the
Django
authentication system, I
wanted a way for an administrator to view the site as if they were
a particular user; effectively an equivalent to
su in UNIX-land.
The first easy step was to invent a URL to correspond to this action, which gets
encoded in
urls.py:
(r'^su/(?P<username>.*)/$', 'qlockweb.accounts.views.su', {'redirect_url': '/qlockdata/'}),
That done, the second and final step is to write some view code.
@user_passes_test(lambda u: u.is_staff)
def su(request, username, redirect_url='/'):
su_user = get_object_or_404(User, username=username)
if su_user.is_active:
request.session[SESSION_KEY] = su_user.id
return HttpResponseRedirect(redirect_url)
Seven lines of code and we're done (modulo a bunch of import statements).
Expanding what's going on here:
(r'^su/(?P<username>.*)/$', 'qlockweb.accounts.views.su', {'redirect_url': '/qlockdata/'}),
urls.py
line above; the /(?P<username>.*)/ part of the regexp pulls out "fred" and this gets
passed as a parameter named username into the function su
in qlockweb/accounts/views.py. This function also gets passed a parameter
called redirect_url with value '/qlockdata/'.@user_passes_test(lambda u: u.is_staff)
su function. The
line before the function definition is a Python
decorator: some extra code wrapping the function that gets
executed just before the function itself is executed. This decorator needs some expansion of its own:
@user_passes_test(lambda u: u.is_staff)
@user_passes_test(lambda u: u.is_staff)
contrib/auth/decorators.py); its first
argument test_func is a function that does the test. This test function is given
a single parameter: the current User. If the test function returns
true, the wrapped view code is called; if not, then the user gets redirected to a login page.
@user_passes_test(lambda u: u.is_staff)
is_this_a_staff_user(u) that
checks whether its argument u is an administrator. However, as this is the
only place that the function is used, we don't bother to give it a name—we just use
a lambda expression
to give the definition right here and now.@user_passes_test(lambda u: u.is_staff)
User class
that indicates whether the user is an administrator or not.def su(request, username, redirect_url='/'):
su function itself, and if we've got this far we're guaranteed that the person viewing the
page is logged in as an is_staff user. The function has the username
and redirect_url parameters mentioned earlier; it also has a request
parameter that holds all of the information about the original web request (in a
HttpRequest
object).su_user = get_object_or_404(User, username=username)
User object for the username that was
specified—fred in other words. If there isn't a user called fred, then
a Http404 exception gets raised, which will percolate up the stack and display a (surprise,
surprise) 404 page.if su_user.is_active:
is_active field in the standard User model.request.session[SESSION_KEY] = su_user.id
return HttpResponseRedirect(redirect_url)
redirect_url page.(Statutory disclaimer: I am not a security expert, nor do I play one on TV. Adding this to a production system is probably not a good idea.)
Another Django snippet: I finally
discovered
that the follow argument to the standard
Manipulators
allows you to list fields in the model that the form should leave untouched. Very helpful: the
end result is more compact
and less brittle than the code I'd put together to manually override all of the hidden fields.
[A:37385 B:3278 C:346 D:9187 E:62544 Total:112740]