Mark
Mark

Reputation: 19969

Django change request.path in middleware (to authenticate by token in url)

Dear omnoscient beings at Stackoverflow,

In Django 1.3 I am making a process_request middleware that gets a token from an url, logs the user in (if it's correct) and removes the token from the url. However:

I) Django recommends against accessing POST/GET data in middleware, I'm not really sure why so... Does the same apply to request.path ? https://docs.djangoproject.com/en/dev/topics/http/middleware/#process-view

II) I want to remove the token from the URL, so /album3/pic42/~53Cr3t70K3n/like/ -> /album3/pic42/like/. Changing request.path however does not work. The page will not be found, while

Is there a fix for this, or am I approaching this from the wrong angle entirely?

Thanks in advance!

I just realized that to change it client side, obviously I need a redirect (why didn't I think of that...). However, it would still be useful to be able to rewrite it just server-side without a new request, for example for accessing a personalized image.


P.s.: more details if needed, feel free to skip

I am working on a site that (will) sends personalized emails to users. I would like users to be able to click links in the email and be logged in automatically, by means of a token in the email link. This is in addition to normal login. (I know it's a less secure because people might forward an e-mail, but it's good enough for my site). The url would look something like this: /album3/pic42/~53Cr3t70K3n/like/ (with http://www.site.com stripped, Django does that)

I am writing a middleware to match this and log the user in when appropriate, a authentication backend for accepting a token as valid credentials and a token model.


Middleware process_request function: def process_request(self, request):

    if '/~' in request.path:
        result = re.search('(.*)/~(.+?)/(.*)', request.path)
        (uidb36, token) = result.group(2).split('-', 2)
        user = authenticate(uidb36 = uidb36, token = token)
        if user: login(request, user)
        return HttpResponseRedirect('%s/%s' % (result.group(1), result.group(3)) + ('?' + '&'.join('='.join(item) for item in request.GET.items()) if request.GET else ''))
    return None

Right now it works with redirects, I'd like to also be able to do it internally.

Upvotes: 4

Views: 3747

Answers (2)

Ivan Kharlamov
Ivan Kharlamov

Reputation: 1933

If you don't want to mess up with upload handlers, I have a better solution for you:

  1. Create a rule in your urls.py to specifically catch visits with tokens

    Put it at the start of the urlpatterns to make sure that it will be evaluated first. Something like this will do:

    (r'/~', 'my_app_name.my_redirect_view'),
    
  2. Create a view:

    def my_redirect_view(request):
        #Compiled regular expressions work much faster
        beloved_tokens = re.compile(r'(.*)/~(.+?)/(.*)')
        result = beloved_tokens.search(request.path)
        try:
            (uidb36, token) = result.group(2).split('-', 2)
            path_end = result.group(3)
        # We use "try" to be sure that no one had
        # messed with our beloved tokens:
        except AttributeError: 
            raise Http404
        else:
            user = authenticate(uidb36 = uidb36, token = token)
            if user: 
                login(request, user)
                return HttpResponseRedirect('%s/%s' % (result.group(1), result.group(3)) + ('?' + '&'.join('='.join(item) for item in request.GET.items()) if request.GET else ''))
            else:
                raise Http404
    

Upvotes: 2

Anurag Uniyal
Anurag Uniyal

Reputation: 88717

IMO changing request.path is more bad(if it can be called bad) compared to accessing GET/POST parameters, so just pass token as GET parameter and login based on token and do not redirect or do request.path modifications

I see token as a added attribute to a valid url, hence a middleware acknowledges that and does something with that token, but url still is handled by the correct view, so middleware to me seems a very logical fit here.

Upvotes: 1

Related Questions