Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django user impersonation by admin

I have a Django app. When logged in as an admin user, I want to be able to pass a secret parameter in the URL and have the whole site behave as if I were another user.

Let's say I have the URL /my-profile/ which shows the currently logged in user's profile. I want to be able to do something like /my-profile/?__user_id=123 and have the underlying view believe that I am actually the user with ID 123 (thus render that user's profile).

Why do I want that?

Simply because it's much easier to reproduce certain bugs that only appear in a single user's account.

My questions:

  1. What would be the easiest way to implement something like this?

  2. Is there any security concern I should have in mind when doing this? Note that I (obviously) only want to have this feature for admin users, and our admin users have full access to the source code, database, etc. anyway, so it's not really a "backdoor"; it just makes it easier to access a user's account.

like image 774
ibz Avatar asked Feb 11 '10 07:02

ibz


3 Answers

I don't have enough reputation to edit or reply yet (I think), but I found that although ionaut's solution worked in simple cases, a more robust solution for me was to use a session variable. That way, even AJAX requests are served correctly without modifying the request URL to include a GET impersonation parameter.

class ImpersonateMiddleware(object):
    def process_request(self, request):
        if request.user.is_superuser and "__impersonate" in request.GET:
            request.session['impersonate_id'] = int(request.GET["__impersonate"])
        elif "__unimpersonate" in request.GET:
            del request.session['impersonate_id']
        if request.user.is_superuser and 'impersonate_id' in request.session:
            request.user = User.objects.get(id=request.session['impersonate_id'])

Usage:

log in: http://localhost/?__impersonate=[USERID]
log out (back to admin): http://localhost/?__unimpersonate=True
like image 116
Charles Offenbacher Avatar answered Oct 31 '22 23:10

Charles Offenbacher


It looks like quite a few other people have had this problem and have written re-usable apps to do this and at least some are listed on the django packages page for user switching. The most active at time of writing appear to be:

  • django-hijack puts a "hijack" button in the user list in the admin, along with a bit at the top of page for while you've hijacked an account.
  • impostor means you can login with username "me as other" and your own password
  • django-impersonate sets up URLs to start impersonating a user, stop, search etc
like image 15
Hamish Downer Avatar answered Oct 31 '22 22:10

Hamish Downer


I solved this with a simple middleware. It also handles redirects (that is, the GET parameter is preserved during a redirect). Here it is:

class ImpersonateMiddleware(object):
    def process_request(self, request):
        if request.user.is_superuser and "__impersonate" in request.GET:
            request.user = models.User.objects.get(id=int(request.GET["__impersonate"]))

    def process_response(self, request, response):
        if request.user.is_superuser and "__impersonate" in request.GET:
            if isinstance(response, http.HttpResponseRedirect):
                location = response["Location"]
                if "?" in location:
                    location += "&"
                else:
                    location += "?"
                location += "__impersonate=%s" % request.GET["__impersonate"]
                response["Location"] = location
        return response
like image 11
ibz Avatar answered Oct 31 '22 22:10

ibz