How do Django authorization decorators (such as: login required) work?

Viewed 982
1 Answers

I don't know of anywhere that has a line-by-line documentation of these decorators, but here's my take on it.

def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):

This function serves as the basis for Django's decorator based authentication. It accepts a test function which will have the user passed to it to determine whether or not that user has access to the decorated view.

    def decorator(view_func):
        @wraps(view_func)
        def _wrapped_view(request, *args, **kwargs):

This chunk of code is just standard Python decorator stuff - if you understand decorators there's not really anything to explain.

            if test_func(request.user):
                return view_func(request, *args, **kwargs)

Here the test function is called with the user. If the user passes the test the original view function is immediately returned and no further action is taken.

            path = request.build_absolute_uri()
            resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)

Retrieve the current request's url and the login url. The login url can be passed to the user_passes_test decorator or the default value in the Django settings can be used.

            # If the login url is the same scheme and net location then just
            # use the path as the "next" url.
            login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
            current_scheme, current_netloc = urlparse(path)[:2]

Retrieve the HTTP scheme (http or https) and the netloc (www.example.com plus the port if applicable) from both the current url and the login url.

            if ((not login_scheme or login_scheme == current_scheme) and
                    (not login_netloc or login_netloc == current_netloc)):
                path = request.get_full_path()

If the HTTP scheme and the netloc for the two urls match then the path is set to the relative url rather than the absolute url.

            from django.contrib.auth.views import redirect_to_login
            return redirect_to_login(
                path, resolved_login_url, redirect_field_name)

Redirect the request to the login page. redirect_to_login will send the user to get logged in with a ?next= GET parameter equal to the current path.

        return _wrapped_view
    return decorator

Finishing decorator stuff.

login_required is just a shortcut for user_passes_test which already supplies the test_func - a simple function which checks the value of user.is_authenticated.

permission_required Does the same but takes the name of a permission or a list of permission names and checks that the user has those permissions. permission_required also has the added feature that you can pass raise_exception=True to raise a 403 instead of redirecting to the login url.

Related