Opened 9 years ago

Closed 9 years ago

#24276 closed Bug (wontfix)

django.contrib.auth.decorators.user_passes_test doesn't pass *args and *kwargs to resolve_url

Reported by: rubengrill Owned by: nobody
Component: contrib.auth Version: 1.7
Severity: Normal Keywords: login_url resolve_url login_required user_passes_test
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

When using the decorator django.contrib.auth.decorators.user_passes_test or dependent decorators login_required and permission_required, the given login_url can't be reversed when the url contains at least one parameter, because when reverse_url is called, the *args and *kwargs parameters are not passed.

urlpatterns = patterns('',
    url(r'^(?P<conference>[^/]+)/', include(patterns('',
        url(r'^$', conference_root, name='conference_root'),
        url(r'^admin/$', conference_admin, name='conference_admin'),
    )))
)
...
@login_required(login_url='conference:conference_root')
def conference_admin(request, conference):
    return render(request, 'conference/admin.html', {'conference': conference})
Reverse for 'conference_root' with arguments '()' and keyword arguments '{}' not found. 1 pattern(s) tried: ['(?P<conference>[^/]+)/$']

The *args and *kwargs parameters should be passed to resolve_url:

def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
    """
    Decorator for views that checks that the user passes the given test,
    redirecting to the log-in page if necessary. The test should be a callable
    that takes the user object and returns True if the user passes.
    """

    def decorator(view_func):
        @wraps(view_func, assigned=available_attrs(view_func))
        def _wrapped_view(request, *args, **kwargs):
            if test_func(request.user):
                return view_func(request, *args, **kwargs)
            path = request.build_absolute_uri()
#
#           resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
#
            resolved_login_url = resolve_url(login_url or settings.LOGIN_URL, *args, **kwargs)
            # 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]
            if ((not login_scheme or login_scheme == current_scheme) and
                    (not login_netloc or login_netloc == current_netloc)):
                path = request.get_full_path()
            from django.contrib.auth.views import redirect_to_login
            return redirect_to_login(
                path, resolved_login_url, redirect_field_name)
        return _wrapped_view
    return decorator

Attachments (1)

24276-test.diff (2.9 KB ) - added by Tim Graham 9 years ago.

Download all attachments as: .zip

Change History (3)

comment:1 by Tim Graham, 9 years ago

Easy pickings: unset

I think there is a problem with your approach and that is that if I use the decorator on a page with some capture parameters but the login page doesn't use those same parameters, resolve_url() won't work. I'm attaching a modification to one of Django's current tests that demonstrates the issue. I'll leave the ticket open if you want to think about other solutions, but it might be easiest for you to use a custom decorator for your use case.

by Tim Graham, 9 years ago

Attachment: 24276-test.diff added

comment:2 by Tim Graham, 9 years ago

Resolution: wontfix
Status: newclosed

Please reopen if you can make a reply, thanks.

Note: See TracTickets for help on using tickets.
Back to Top