Skip to content

Commit c69146c

Browse files
committed
implemented async auth mixins
1 parent afd4322 commit c69146c

File tree

3 files changed

+100
-0
lines changed

3 files changed

+100
-0
lines changed

django_async_extensions/acontrib/__init__.py

Whitespace-only changes.

django_async_extensions/acontrib/auth/__init__.py

Whitespace-only changes.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
from urllib.parse import urlparse
2+
3+
from asgiref.sync import sync_to_async
4+
from django.contrib.auth.mixins import AccessMixin
5+
from django.contrib.auth.views import redirect_to_login
6+
from django.core.exceptions import PermissionDenied, ImproperlyConfigured
7+
from django.shortcuts import resolve_url
8+
9+
10+
class AsyncAccessMixin(AccessMixin):
11+
async def handle_no_permission(self):
12+
user = await self.request.auser()
13+
if self.raise_exception or user.is_authenticated:
14+
raise PermissionDenied(self.get_permission_denied_message())
15+
16+
path = self.request.build_absolute_uri()
17+
resolved_login_url = resolve_url(self.get_login_url())
18+
# If the login url is the same scheme and net location then use the
19+
# path as the "next" url.
20+
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
21+
current_scheme, current_netloc = urlparse(path)[:2]
22+
if (not login_scheme or login_scheme == current_scheme) and (
23+
not login_netloc or login_netloc == current_netloc
24+
):
25+
path = self.request.get_full_path()
26+
return redirect_to_login(
27+
path,
28+
resolved_login_url,
29+
self.get_redirect_field_name(),
30+
)
31+
32+
33+
class AsyncLoginRequiredMixin(AsyncAccessMixin):
34+
"""Verify that the current user is authenticated."""
35+
36+
async def dispatch(self, request, *args, **kwargs):
37+
user = await request.auser()
38+
if not user.is_authenticated:
39+
return await self.handle_no_permission()
40+
return await super().dispatch(request, *args, **kwargs)
41+
42+
43+
class AsyncPermissionRequiredMixin(AsyncAccessMixin):
44+
"""Verify that the current user has all specified permissions."""
45+
46+
permission_required = None
47+
48+
def get_permission_required(self):
49+
"""
50+
Override this method to override the permission_required attribute.
51+
Must return an iterable.
52+
"""
53+
if self.permission_required is None:
54+
raise ImproperlyConfigured(
55+
f"{self.__class__.__name__} is missing the "
56+
f"permission_required attribute. Define "
57+
f"{self.__class__.__name__}.permission_required, or override "
58+
f"{self.__class__.__name__}.get_permission_required()."
59+
)
60+
if isinstance(self.permission_required, str):
61+
perms = (self.permission_required,)
62+
else:
63+
perms = self.permission_required
64+
return perms
65+
66+
async def has_permission(self):
67+
perms = self.get_permission_required()
68+
user = await self.request.auser()
69+
return await sync_to_async(user.has_perms)(perms)
70+
71+
async def dispatch(self, request, *args, **kwargs):
72+
if not await self.has_permission():
73+
return await self.handle_no_permission()
74+
return await super().dispatch(request, *args, **kwargs)
75+
76+
77+
class AsyncUserPassesTestMixin(AsyncAccessMixin):
78+
"""
79+
Deny a request with a permission error if the test_func() method returns
80+
False.
81+
"""
82+
83+
def test_func(self):
84+
raise NotImplementedError(
85+
"{} is missing the implementation of the test_func() method.".format(
86+
self.__class__.__name__
87+
)
88+
)
89+
90+
def get_test_func(self):
91+
"""
92+
Override this method to use a different test_func method.
93+
"""
94+
return self.test_func
95+
96+
async def dispatch(self, request, *args, **kwargs):
97+
user_test_result = self.get_test_func()()
98+
if not user_test_result:
99+
return await self.handle_no_permission()
100+
return await super().dispatch(request, *args, **kwargs)

0 commit comments

Comments
 (0)