Created
September 15, 2011 20:26
-
-
Save jacobian/1220375 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/django/contrib/auth/decorators.py b/django/contrib/auth/decorators.py | |
index 5805a31..a07a4d9 100644 | |
--- a/django/contrib/auth/decorators.py | |
+++ b/django/contrib/auth/decorators.py | |
@@ -1,10 +1,11 @@ | |
+import inspect | |
import urlparse | |
from functools import wraps | |
from django.conf import settings | |
from django.contrib.auth import REDIRECT_FIELD_NAME | |
from django.core.exceptions import PermissionDenied | |
from django.utils.decorators import available_attrs | |
- | |
+from django.http import HttpRequest | |
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): | |
""" | |
@@ -13,11 +14,46 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE | |
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): | |
+ def decorator(obj): | |
+ # Case 1: decorated class. | |
+ if inspect.isclass(obj): | |
+ # FIXME 1: this should be done in a way that mimics @wraps: the | |
+ # class name should stay the same (or similar), and | |
+ # docstrings should be preserved. | |
+ # FIXME 2: This shouldn't just assume that the object is a CBV and | |
+ # wrap dispatch; it should inspect and figure it out and | |
+ # raise an error if it's the wrong type. | |
+ class LoginProtectedSubclass(obj): | |
+ @user_passes_test(test_func, login_url, redirect_field_name) | |
+ def dispatch(self, *args, **kwargs): | |
+ return super(LoginProtectedSubclass, self).dispatch(*args, **kwargs) | |
+ return LoginProtectedSubclass | |
+ | |
+ # Case 2: decorated function or method. | |
+ # These two case differ: one will have a signature like (request, ...), | |
+ # and the other will be (self, request, ...). Since we need to inspect | |
+ # request this poses a bit of a problem. Now, We don't know which one's | |
+ # which just yet, so we have to wait until the decorator is called to | |
+ # figure it out. | |
+ @wraps(obj, assigned=available_attrs(obj)) | |
+ def _wrapped_view(*args, **kwargs): | |
+ # If this is a function, args[0] will be the request object. If | |
+ # this is a method, args[0] will be self and args[1] will be the | |
+ # request object. We'll avoid just naively looking at args[0] | |
+ # and args[1], though: that'll raise an IndexError if the decorator | |
+ # is used on a non-view-function. That's a useless error message, | |
+ # so we'll raise something a bit nicer instead. | |
+ request = None | |
+ for arg in args[0:2]: | |
+ if isinstance(arg, HttpRequest): | |
+ request = arg | |
+ break | |
+ if request is None: | |
+ # FIXME: better error message, please. | |
+ raise TypeError("View wasn't called with a HttpRequest object.") | |
+ | |
if test_func(request.user): | |
- return view_func(request, *args, **kwargs) | |
+ return obj(*args, **kwargs) | |
path = request.build_absolute_uri() | |
# If the login url is the same scheme and net location then just | |
# use the path as the "next" url. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment