Source

djutils / djutils / auth / views.py

Full commit
import urlparse

from django.conf import settings
from django.http import HttpResponseRedirect
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.shortcuts import render_to_response
from django.template import RequestContext
from djutils.forms import LoginForm


__all__ = ('login', 'logout',)


def validate_redirect(request, redirect_to):
    """Validate the given redirect url. It either needs to be
    relative, or the same hostname as ``request``.
    """

    if redirect_to:
        url = urlparse.urlparse(redirect_to)

        if url.netloc and url.netloc != request.get_host():
            return None

        if ' ' in url.path:
            return None

        # Prevent redirect loops
        if url.path == request.path:
            return None

    return redirect_to


def resolve_redirect(request, redirect_field_name, redirect_to_referrer):
    """Helper that determines where to redirect ``request`` with the
    given redirection options.
    """

    redirect_to = None

    # Try GET field first
    if redirect_field_name:
        redirect_to = request.REQUEST.get(redirect_field_name)

    # Try referrer second.
    if not redirect_to and redirect_to_referrer:
        redirect_to = request.META.get('HTTP_REFERER')

    return validate_redirect(request, redirect_to)


def login(request, template_name='registration/login.html',
          success_url=settings.LOGIN_REDIRECT_URL, on_success=None,
          redirect_field_name=REDIRECT_FIELD_NAME,
          redirect_to_referrer=False, form_class=LoginForm,
          messages = {}, render_response=render_to_response):
    """
    Similar to Django's builtin ``login`` view, but with additional
    features.

    ``messages`` is a key => message dict that allows for custom
    error messages.

    If you pass a callable via ``on_success`` it will run after a
    successful login, just before the redirect. Useful for showing a
    flash, for example.

    Using ``render_response`` you can hook provide a custom template
    backend.
    """

    redirect_to = resolve_redirect(request, redirect_field_name,
                                   redirect_to_referrer)

    # If the user is already logged in, he has no business here
    if request.user.is_authenticated():
        return HttpResponseRedirect(redirect_to  or success_url)

    # Handle submitted login data
    if request.method == "POST":
        login_form = form_class(request, request.POST, messages=messages)
        if login_form.login():
            result = on_success and on_success() or None
            final_redirect = (
                # Make sure we validate any saved redirect that is sent
                # through the form, or something could be injected.
                validate_redirect(request, login_form.cleaned_data['redirect']) or \
                redirect_to or \
                success_url)
            return result or HttpResponseRedirect(final_redirect)
    else:
        # Make sure we save any redirect that we potentially determined
        # as an initial value in the form, or a referrer redirect may be
        # lost, and a GET field redirect may also be lost, albeit more
        # rarely, if the form POST url includes it's own query string.
        login_form = form_class(request, messages=messages,
                               initial={'redirect': redirect_to})

    # Render the view
    return render_response(template_name, {'form': login_form },
        context_instance=RequestContext(request))


def logout(request, next_page=None, template_name='registration/logged_out.html',
           redirect_field_name=REDIRECT_FIELD_NAME, redirect_to_referrer=False):
    """Similar to Django's builtin ``logout`` view, but with additional
    features.

    There is also a slight difference in behavior, in that ``next_page``
    does not disable the use of ``redirect_field_name``. The redirect
    field is checked first, and next_page is only used as a default target
    (Django actually has a bug here, it does it the same way but documents
    it differently, see #11223).
    """

    final_redirect = resolve_redirect(request, redirect_field_name,
                                      redirect_to_referrer) or next_page

    from django.contrib.auth import logout
    logout(request)

    if final_redirect is None:
        return render_to_response(template_name, {
            'title': _('Logged out')
        }, context_instance=RequestContext(request))
    else:
        # Redirect to this page until the session has been cleared.
        return HttpResponseRedirect(final_redirect or request.path)