version 0.0.12: updated JWT views
This commit is contained in:
parent
d5a14ef8c2
commit
663c003119
|
|
@ -19,12 +19,16 @@ from account.models import User
|
||||||
from authorization.models import Application
|
from authorization.models import Application
|
||||||
from authorization.serializers import common as serializers
|
from authorization.serializers import common as serializers
|
||||||
from utils import exceptions as utils_exceptions
|
from utils import exceptions as utils_exceptions
|
||||||
from utils.views import JWTViewMixin
|
from utils.views import (JWTGenericViewMixin,
|
||||||
|
JWTCreateAPIView,
|
||||||
|
JWTDestroyAPIView,
|
||||||
|
JWTUpdateAPIView,
|
||||||
|
JWTRetrieveAPIView)
|
||||||
|
|
||||||
|
|
||||||
# Mixins
|
# Mixins
|
||||||
# JWTAuthView mixin
|
# JWTAuthView mixin
|
||||||
class JWTAuthViewMixin(JWTViewMixin):
|
class JWTAuthViewMixin(JWTCreateAPIView):
|
||||||
"""Mixin for authentication views"""
|
"""Mixin for authentication views"""
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
@ -98,7 +102,7 @@ class OAuth2ViewMixin(CsrfExemptMixin, OAuthLibMixin, BaseOAuth2ViewMixin):
|
||||||
|
|
||||||
|
|
||||||
# Sign in via Facebook
|
# Sign in via Facebook
|
||||||
class OAuth2SignUpView(OAuth2ViewMixin, JWTAuthViewMixin):
|
class OAuth2SignUpView(OAuth2ViewMixin, JWTCreateAPIView):
|
||||||
"""
|
"""
|
||||||
Implements an endpoint to convert a provider token to an access token
|
Implements an endpoint to convert a provider token to an access token
|
||||||
|
|
||||||
|
|
@ -176,7 +180,7 @@ class OAuth2SignUpView(OAuth2ViewMixin, JWTAuthViewMixin):
|
||||||
|
|
||||||
# JWT
|
# JWT
|
||||||
# Sign in via username and password
|
# Sign in via username and password
|
||||||
class SignUpView(JWTAuthViewMixin):
|
class SignUpView(JWTCreateAPIView):
|
||||||
"""View for classic signup"""
|
"""View for classic signup"""
|
||||||
permission_classes = (permissions.AllowAny, )
|
permission_classes = (permissions.AllowAny, )
|
||||||
serializer_class = serializers.SignupSerializer
|
serializer_class = serializers.SignupSerializer
|
||||||
|
|
@ -207,18 +211,37 @@ class SignUpView(JWTAuthViewMixin):
|
||||||
|
|
||||||
|
|
||||||
# Login by username|email + password
|
# Login by username|email + password
|
||||||
class LoginByUsernameOrEmailView(JWTAuthViewMixin):
|
class LoginByUsernameOrEmailView(JWTCreateAPIView):
|
||||||
"""Login by email and password"""
|
"""Login by email and password"""
|
||||||
permission_classes = (permissions.AllowAny,)
|
permission_classes = (permissions.AllowAny,)
|
||||||
serializer_class = serializers.LoginByUsernameOrEmailSerializer
|
serializer_class = serializers.LoginByUsernameOrEmailSerializer
|
||||||
|
|
||||||
|
|
||||||
# Refresh access_token
|
# Refresh access_token
|
||||||
class RefreshTokenView(JWTAuthViewMixin):
|
class RefreshTokenView(JWTGenericViewMixin):
|
||||||
"""Refresh access_token"""
|
"""Refresh access_token"""
|
||||||
permission_classes = (permissions.IsAuthenticated,)
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
serializer_class = serializers.RefreshTokenSerializer
|
serializer_class = serializers.RefreshTokenSerializer
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
_locale = request.COOKIES.get('locale')
|
||||||
|
try:
|
||||||
|
locale = self._check_locale(locale=_locale)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
response = Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
access_token, refresh_token = self._get_tokens_from_cookies(request)
|
||||||
|
except utils_exceptions.LocaleNotExisted:
|
||||||
|
raise utils_exceptions.LocaleNotExisted(locale=_locale)
|
||||||
|
else:
|
||||||
|
return self._put_cookies_in_response(
|
||||||
|
cookies=self._put_data_in_cookies(locale=locale,
|
||||||
|
access_token=access_token,
|
||||||
|
refresh_token=refresh_token),
|
||||||
|
response=response)
|
||||||
|
|
||||||
|
|
||||||
# Logout
|
# Logout
|
||||||
class LogoutView(generics.CreateAPIView):
|
class LogoutView(generics.CreateAPIView):
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
from rest_framework import generics, permissions
|
from rest_framework import generics, permissions
|
||||||
from news.models import News
|
from news.models import News
|
||||||
from news.serializers import common as serializers
|
from news.serializers import common as serializers
|
||||||
from utils.views import JWTViewMixin
|
from utils.views import JWTGenericViewMixin
|
||||||
|
|
||||||
|
|
||||||
class NewsList(JWTViewMixin, generics.ListAPIView):
|
class NewsList(generics.ListAPIView):
|
||||||
"""News list view."""
|
"""News list view."""
|
||||||
queryset = News.objects.all()
|
queryset = News.objects.all()
|
||||||
permission_classes = (permissions.AllowAny, )
|
permission_classes = (permissions.AllowAny, )
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from rest_framework import generics
|
||||||
from translation import models
|
from translation import models
|
||||||
from translation import serializers
|
from translation import serializers
|
||||||
from rest_framework import permissions
|
from rest_framework import permissions
|
||||||
from utils.views import JWTViewMixin
|
from utils.views import JWTGenericViewMixin
|
||||||
|
|
||||||
|
|
||||||
# Mixins
|
# Mixins
|
||||||
|
|
@ -13,7 +13,7 @@ class LanguageViewMixin(generics.GenericAPIView):
|
||||||
|
|
||||||
|
|
||||||
# Views
|
# Views
|
||||||
class LanguageListView(LanguageViewMixin, JWTViewMixin, generics.ListAPIView):
|
class LanguageListView(LanguageViewMixin, generics.ListAPIView):
|
||||||
"""List view for model Language"""
|
"""List view for model Language"""
|
||||||
permission_classes = (permissions.AllowAny, )
|
permission_classes = (permissions.AllowAny, )
|
||||||
serializer_class = serializers.LanguageSerializer
|
serializer_class = serializers.LanguageSerializer
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,20 @@ from collections import namedtuple
|
||||||
from translation import models as translation_models
|
from translation import models as translation_models
|
||||||
from utils import exceptions
|
from utils import exceptions
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
|
||||||
# JWT
|
# JWT
|
||||||
# Login base view mixin
|
# Login base view mixin
|
||||||
class JWTViewMixin(generics.GenericAPIView):
|
class JWTGenericViewMixin(generics.GenericAPIView):
|
||||||
"""JWT view mixin"""
|
"""JWT view mixin"""
|
||||||
|
|
||||||
ACCESS_TOKEN_HTTP = True
|
ACCESS_TOKEN_HTTP_ONLY = True
|
||||||
ACCESS_TOKEN_SECURE = False
|
ACCESS_TOKEN_SECURE = False
|
||||||
|
|
||||||
REFRESH_TOKEN_HTTP = True
|
REFRESH_TOKEN_HTTP_ONLY = True
|
||||||
REFRESH_TOKEN_SECURE = False
|
REFRESH_TOKEN_SECURE = False
|
||||||
COOKIE = namedtuple('COOKIE', ['key', 'value', 'http', 'secure'])
|
COOKIE = namedtuple('COOKIE', ['key', 'value', 'http_only', 'secure'])
|
||||||
|
|
||||||
def _check_locale(self, locale: str):
|
def _check_locale(self, locale: str):
|
||||||
|
|
||||||
|
|
@ -24,10 +25,7 @@ class JWTViewMixin(generics.GenericAPIView):
|
||||||
raise exceptions.LocaleNotExisted()
|
raise exceptions.LocaleNotExisted()
|
||||||
return locale
|
return locale
|
||||||
|
|
||||||
def _put_data_in_cookies(self,
|
def _put_data_in_cookies(self, locale: str, access_token: str, refresh_token: str):
|
||||||
locale: str,
|
|
||||||
access_token: str,
|
|
||||||
refresh_token: str):
|
|
||||||
"""
|
"""
|
||||||
CHECK locale in cookies and PUT access and refresh tokens there.
|
CHECK locale in cookies and PUT access and refresh tokens there.
|
||||||
cookies it is list that contain namedtuples
|
cookies it is list that contain namedtuples
|
||||||
|
|
@ -36,22 +34,21 @@ class JWTViewMixin(generics.GenericAPIView):
|
||||||
COOKIES = list()
|
COOKIES = list()
|
||||||
|
|
||||||
# Create locale namedtuple
|
# Create locale namedtuple
|
||||||
locale = self.COOKIE(key='locale',
|
_locale = self.COOKIE(key='locale',
|
||||||
value=locale,
|
value=locale,
|
||||||
http=True,
|
http_only=True,
|
||||||
secure=False)
|
secure=False)
|
||||||
COOKIES.append(locale)
|
|
||||||
|
|
||||||
# Write to cookie access and refresh token with secure flag
|
# Write to cookie access and refresh token with secure flag
|
||||||
_access_token = self.COOKIE(key='access_token',
|
_access_token = self.COOKIE(key='access_token',
|
||||||
value=access_token,
|
value=access_token,
|
||||||
http=self.ACCESS_TOKEN_HTTP,
|
http_only=self.ACCESS_TOKEN_HTTP_ONLY,
|
||||||
secure=self.ACCESS_TOKEN_SECURE)
|
secure=self.ACCESS_TOKEN_SECURE)
|
||||||
_refresh_token = self.COOKIE(key='refresh_token',
|
_refresh_token = self.COOKIE(key='refresh_token',
|
||||||
value=refresh_token,
|
value=refresh_token,
|
||||||
http=self.REFRESH_TOKEN_HTTP,
|
http_only=self.REFRESH_TOKEN_HTTP_ONLY,
|
||||||
secure=self.REFRESH_TOKEN_SECURE)
|
secure=self.REFRESH_TOKEN_SECURE)
|
||||||
COOKIES.extend((_access_token, _refresh_token))
|
COOKIES.extend((_locale, _access_token, _refresh_token))
|
||||||
return COOKIES
|
return COOKIES
|
||||||
|
|
||||||
def _put_cookies_in_response(self, cookies: list, response: Response):
|
def _put_cookies_in_response(self, cookies: list, response: Response):
|
||||||
|
|
@ -59,7 +56,8 @@ class JWTViewMixin(generics.GenericAPIView):
|
||||||
for cookie in cookies:
|
for cookie in cookies:
|
||||||
response.set_cookie(key=cookie.key,
|
response.set_cookie(key=cookie.key,
|
||||||
value=cookie.value,
|
value=cookie.value,
|
||||||
secure=cookie.secure)
|
secure=cookie.secure,
|
||||||
|
httponly=cookie.http_only)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def _get_tokens_from_cookies(self, request, cookies: dict = None):
|
def _get_tokens_from_cookies(self, request, cookies: dict = None):
|
||||||
|
|
@ -67,13 +65,43 @@ class JWTViewMixin(generics.GenericAPIView):
|
||||||
_cookies = request.COOKIES or cookies
|
_cookies = request.COOKIES or cookies
|
||||||
return [self.COOKIE(key='access_token',
|
return [self.COOKIE(key='access_token',
|
||||||
value=_cookies.get('access_token'),
|
value=_cookies.get('access_token'),
|
||||||
http=self.ACCESS_TOKEN_HTTP,
|
http_only=self.ACCESS_TOKEN_HTTP_ONLY,
|
||||||
secure=self.ACCESS_TOKEN_SECURE),
|
secure=self.ACCESS_TOKEN_SECURE),
|
||||||
self.COOKIE(key='refresh_token',
|
self.COOKIE(key='refresh_token',
|
||||||
value=_cookies.get('refresh_token'),
|
value=_cookies.get('refresh_token'),
|
||||||
http=self.REFRESH_TOKEN_HTTP,
|
http_only=self.REFRESH_TOKEN_HTTP_ONLY,
|
||||||
secure=self.REFRESH_TOKEN_SECURE)]
|
secure=self.REFRESH_TOKEN_SECURE)]
|
||||||
|
|
||||||
|
|
||||||
|
class JWTCreateAPIView(JWTGenericViewMixin, generics.CreateAPIView):
|
||||||
|
"""
|
||||||
|
Concrete view for creating a model instance.
|
||||||
|
"""
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
_locale = request.COOKIES.get('locale')
|
||||||
|
try:
|
||||||
|
locale = self._check_locale(locale=_locale)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
serializer.save()
|
||||||
|
|
||||||
|
response = Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
access_token, refresh_token = self._get_tokens_from_cookies(request)
|
||||||
|
except exceptions.LocaleNotExisted:
|
||||||
|
raise exceptions.LocaleNotExisted(locale=_locale)
|
||||||
|
else:
|
||||||
|
return self._put_cookies_in_response(
|
||||||
|
cookies=self._put_data_in_cookies(locale=locale,
|
||||||
|
access_token=access_token,
|
||||||
|
refresh_token=refresh_token),
|
||||||
|
response=response)
|
||||||
|
|
||||||
|
|
||||||
|
class JWTRetrieveAPIView(JWTGenericViewMixin, generics.RetrieveAPIView):
|
||||||
|
"""
|
||||||
|
Concrete view for retrieving a model instance.
|
||||||
|
"""
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
"""Implement GET method"""
|
"""Implement GET method"""
|
||||||
_locale = request.COOKIES.get('locale')
|
_locale = request.COOKIES.get('locale')
|
||||||
|
|
@ -87,7 +115,7 @@ class JWTViewMixin(generics.GenericAPIView):
|
||||||
response = self.get_paginated_response(serializer.data)
|
response = self.get_paginated_response(serializer.data)
|
||||||
else:
|
else:
|
||||||
serializer = self.get_serializer(queryset, many=True)
|
serializer = self.get_serializer(queryset, many=True)
|
||||||
response = Response(serializer.data)
|
response = Response(serializer.data, status.HTTP_200_OK)
|
||||||
|
|
||||||
access_token, refresh_token = self._get_tokens_from_cookies(request)
|
access_token, refresh_token = self._get_tokens_from_cookies(request)
|
||||||
|
|
||||||
|
|
@ -99,3 +127,62 @@ class JWTViewMixin(generics.GenericAPIView):
|
||||||
access_token=access_token,
|
access_token=access_token,
|
||||||
refresh_token=refresh_token),
|
refresh_token=refresh_token),
|
||||||
response=response)
|
response=response)
|
||||||
|
|
||||||
|
|
||||||
|
class JWTDestroyAPIView(JWTGenericViewMixin, generics.DestroyAPIView):
|
||||||
|
"""
|
||||||
|
Concrete view for deleting a model instance.
|
||||||
|
"""
|
||||||
|
def delete(self, request, *args, **kwargs):
|
||||||
|
_locale = request.COOKIES.get('locale')
|
||||||
|
try:
|
||||||
|
locale = self._check_locale(locale=_locale)
|
||||||
|
instance = self.get_object()
|
||||||
|
instance.delete()
|
||||||
|
response = Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
access_token, refresh_token = self._get_tokens_from_cookies(request)
|
||||||
|
except exceptions.LocaleNotExisted:
|
||||||
|
raise exceptions.LocaleNotExisted(locale=_locale)
|
||||||
|
else:
|
||||||
|
return self._put_cookies_in_response(
|
||||||
|
cookies=self._put_data_in_cookies(locale=locale,
|
||||||
|
access_token=access_token,
|
||||||
|
refresh_token=refresh_token),
|
||||||
|
response=response)
|
||||||
|
|
||||||
|
|
||||||
|
class JWTUpdateAPIView(JWTGenericViewMixin, generics.UpdateAPIView):
|
||||||
|
"""
|
||||||
|
Concrete view for updating a model instance.
|
||||||
|
"""
|
||||||
|
def put(self, request, *args, **kwargs):
|
||||||
|
_locale = request.COOKIES.get('locale')
|
||||||
|
try:
|
||||||
|
locale = self._check_locale(locale=_locale)
|
||||||
|
partial = kwargs.pop('partial', False)
|
||||||
|
instance = self.get_object()
|
||||||
|
serializer = self.get_serializer(instance, data=request.data, partial=partial)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
serializer.save()
|
||||||
|
|
||||||
|
if getattr(instance, '_prefetched_objects_cache', None):
|
||||||
|
# If 'prefetch_related' has been applied to a queryset, we need to
|
||||||
|
# forcibly invalidate the prefetch cache on the instance.
|
||||||
|
instance._prefetched_objects_cache = {}
|
||||||
|
|
||||||
|
response = Response(serializer.data)
|
||||||
|
access_token, refresh_token = self._get_tokens_from_cookies(request)
|
||||||
|
except exceptions.LocaleNotExisted:
|
||||||
|
raise exceptions.LocaleNotExisted(locale=_locale)
|
||||||
|
else:
|
||||||
|
return self._put_cookies_in_response(
|
||||||
|
cookies=self._put_data_in_cookies(locale=locale,
|
||||||
|
access_token=access_token,
|
||||||
|
refresh_token=refresh_token),
|
||||||
|
response=response)
|
||||||
|
|
||||||
|
def patch(self, request, *args, **kwargs):
|
||||||
|
kwargs['partial'] = True
|
||||||
|
return self.put(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,6 @@ SEND_SMS = False
|
||||||
SMS_CODE_SHOW = True
|
SMS_CODE_SHOW = True
|
||||||
|
|
||||||
DOMAIN_URI = 'localhost:8000'
|
DOMAIN_URI = 'localhost:8000'
|
||||||
|
|
||||||
|
# Increase access token lifetime for local deploy
|
||||||
|
SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'] = timedelta(days=365)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user