Code

Ticket #4376: django.contrib.auth.decorators.3.diff

File django.contrib.auth.decorators.3.diff, 8.9 KB (added by anonymous, 7 years ago)

updated version with tests

Line 
1Index: django/contrib/auth/decorators.py
2===================================================================
3--- django/contrib/auth/decorators.py   (revision 5312)
4+++ django/contrib/auth/decorators.py   (working copy)
5@@ -8,27 +8,16 @@
6     redirecting to the log-in page if necessary. The test should be a callable
7     that takes the user object and returns True if the user passes.
8     """
9-    if not login_url:
10-        from django.conf import settings
11-        login_url = settings.LOGIN_URL
12-    def _dec(view_func):
13-        def _checklogin(request, *args, **kwargs):
14-            if test_func(request.user):
15-                return view_func(request, *args, **kwargs)
16-            return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, quote(request.get_full_path())))
17-        _checklogin.__doc__ = view_func.__doc__
18-        _checklogin.__dict__ = view_func.__dict__
19+    def decorate(view_func):
20+        return _CheckLogin(view_func, test_func, login_url)
21+    return decorate
22 
23-        return _checklogin
24-    return _dec
25-
26-login_required = user_passes_test(lambda u: u.is_authenticated())
27-login_required.__doc__ = (
28+def login_required(view_func):
29     """
30     Decorator for views that checks that the user is logged in, redirecting
31     to the log-in page if necessary.
32     """
33-    )
34+    return _CheckLogin(view_func, lambda u: u.is_authenticated())
35 
36 def permission_required(perm, login_url=None):
37     """
38@@ -37,3 +26,34 @@
39     """
40     return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
41 
42+
43+class _CheckLogin(object):
44+    """
45+    Class that checks that the user passes the given test, redirecting to
46+    the log-in page if necessary. If the test is passed, the view function
47+    is invoked. The test should be a callable that takes the user object
48+    and returns True if the user passes.
49+
50+    We use a class here so that we can define __get__. This way, when a
51+    _CheckLogin object is used as a method decorator, the view function
52+    is properly bound to its instance.
53+    """
54+    def __init__(self, view_func, test_func, login_url=None):
55+        if not login_url:
56+            from django.conf import settings
57+            login_url = settings.LOGIN_URL
58+        self.view_func = view_func
59+        self.test_func = test_func
60+        self.login_url = login_url
61+       
62+    def __get__(self, obj, cls=None):
63+        view_func = self.view_func.__get__(obj, cls)
64+        return _CheckLogin(view_func, self.test_func, self.login_url)
65+   
66+    def __call__(self, request, *args, **kwargs):
67+        if self.test_func(request.user):
68+            return self.view_func(request, *args, **kwargs)
69+        path = quote(request.get_full_path())
70+        tup = self.login_url, REDIRECT_FIELD_NAME, path
71+        return HttpResponseRedirect('%s?%s=%s' % tup)
72+
73Index: tests/modeltests/test_client/models.py
74===================================================================
75--- tests/modeltests/test_client/models.py      (revision 5312)
76+++ tests/modeltests/test_client/models.py      (working copy)
77@@ -218,6 +218,49 @@
78         self.assertEqual(response.status_code, 200)
79         self.assertEqual(response.context['user'].username, 'testclient')
80 
81+    def test_view_with_method_login(self):
82+        "Request a page that is protected with a @login_required method"
83+       
84+        # Get the page without logging in. Should result in 302.
85+        response = self.client.get('/test_client/login_protected_method_view/')
86+        self.assertRedirects(response, '/accounts/login/')
87+       
88+        # Log in
89+        self.client.login(username='testclient', password='password')
90+
91+        # Request a page that requires a login
92+        response = self.client.get('/test_client/login_protected_method_view/')
93+        self.assertEqual(response.status_code, 200)
94+        self.assertEqual(response.context['user'].username, 'testclient')
95+
96+    def test_view_with_permissions(self):
97+        "Request a page that is protected with @permission_required"
98+       
99+        # Get the page without logging in. Should result in 302.
100+        response = self.client.get('/test_client/permission_protected_view/')
101+        self.assertRedirects(response, '/accounts/login/')
102+       
103+        # Log in with wrong permissions. Should result in 302.
104+        self.client.login(username='testclient', password='password')
105+        response = self.client.get('/test_client/permission_protected_view/')
106+        self.assertRedirects(response, '/accounts/login/')
107+
108+        # TODO: Log in with right permissions and request the page again
109+
110+    def test_view_with_method_permissions(self):
111+        "Request a page that is protected with a @permission_required method"
112+       
113+        # Get the page without logging in. Should result in 302.
114+        response = self.client.get('/test_client/permission_protected_method_view/')
115+        self.assertRedirects(response, '/accounts/login/')
116+       
117+        # Log in with wrong permissions. Should result in 302.
118+        self.client.login(username='testclient', password='password')
119+        response = self.client.get('/test_client/permission_protected_method_view/')
120+        self.assertRedirects(response, '/accounts/login/')
121+
122+        # TODO: Log in with right permissions and request the page again
123+
124     def test_view_with_bad_login(self):
125         "Request a page that is protected with @login, but use bad credentials"
126 
127Index: tests/modeltests/test_client/urls.py
128===================================================================
129--- tests/modeltests/test_client/urls.py        (revision 5312)
130+++ tests/modeltests/test_client/urls.py        (working copy)
131@@ -13,6 +13,9 @@
132     (r'^form_view/$', views.form_view),
133     (r'^form_view_with_template/$', views.form_view_with_template),
134     (r'^login_protected_view/$', views.login_protected_view),
135+    (r'^permission_protected_view/$', views.permission_protected_view),
136+    (r'^login_protected_method_view/$', views.login_protected_method_view),
137+    (r'^permission_protected_method_view/$', views.permission_protected_method_view),
138     (r'^session_view/$', views.session_view),
139     (r'^broken_view/$', views.broken_view),
140     (r'^mail_sending_view/$', views.mail_sending_view),
141Index: tests/modeltests/test_client/views.py
142===================================================================
143--- tests/modeltests/test_client/views.py       (revision 5312)
144+++ tests/modeltests/test_client/views.py       (working copy)
145@@ -2,7 +2,7 @@
146 from django.core.mail import EmailMessage, SMTPConnection
147 from django.template import Context, Template
148 from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound
149-from django.contrib.auth.decorators import login_required
150+from django.contrib.auth.decorators import login_required, permission_required
151 from django.newforms.forms import Form
152 from django.newforms import fields
153 from django.shortcuts import render_to_response
154@@ -112,10 +112,43 @@
155 def login_protected_view(request):
156     "A simple view that is login protected."
157     t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
158-    c = Context({'user': request.user})
159+    c = Context({'user': request.user})
160     
161     return HttpResponse(t.render(c))
162-login_protected_view = login_required(login_protected_view)
163+login_protected_view = login_required(login_protected_view)
164+
165+def permission_protected_view(request):
166+    "A simple view that is permission protected."
167+    t = Template('This is a permission protected test. '
168+                 'Username is {{ user.username }}. '
169+                 'Permissions are {{ user.get_all_permissions }}.' ,
170+                 name='Permissions Template')
171+    c = Context({'user': request.user})
172+    return HttpResponse(t.render(c))
173+permission_protected_view = permission_required('modeltests.test_perm')(permission_protected_view)
174+
175+class _ViewManager(object):
176+    def login_protected_view(self, request):
177+        t = Template('This is a login protected test using a method. '
178+                     'Username is {{ user.username }}.',
179+                     name='Login Method Template')
180+        c = Context({'user': request.user})
181+        return HttpResponse(t.render(c))
182+    login_protected_view = login_required(login_protected_view)
183+
184+    def permission_protected_view(self, request):
185+        t = Template('This is a permission protected test using a method. '
186+                     'Username is {{ user.username }}. '
187+                     'Permissions are {{ user.get_all_permissions }}.' ,
188+                     name='Permissions Template')
189+        c = Context({'user': request.user})
190+        return HttpResponse(t.render(c))
191+    permission_protected_view = permission_required('modeltests.test_perm')(permission_protected_view)
192+
193+_view_manager = _ViewManager()
194+login_protected_method_view = _view_manager.login_protected_view
195+permission_protected_method_view = _view_manager.permission_protected_view
196+
197 
198 def session_view(request):
199     "A view that modifies the session"