gault-millau/apps/authorization/views/common.py

303 lines
12 KiB
Python

"""Common views for application Account"""
import json
from braces.views import CsrfExemptMixin
from django.conf import settings
from django.urls import reverse
from django.utils.encoding import force_text
from django.utils.http import urlsafe_base64_decode
from django.utils.translation import gettext_lazy as _
from oauth2_provider.oauth2_backends import OAuthLibCore
from oauth2_provider.settings import oauth2_settings
from oauth2_provider.views.mixins import OAuthLibMixin
from rest_framework import generics
from rest_framework import permissions
from rest_framework import status
from rest_framework.response import Response
from rest_framework_simplejwt import tokens as jwt_tokens
from rest_framework_social_oauth2.oauth2_backends import KeepRequestCore
from rest_framework_social_oauth2.oauth2_endpoints import SocialTokenServer
from account.models import User
from authorization import tasks
from authorization.models import Application
from authorization.serializers import common as serializers
from utils import exceptions as utils_exceptions
from utils.models import gm_token_generator
from utils.views import (JWTGenericViewMixin,
JWTCreateAPIView)
# Mixins
# JWTAuthView mixin
class JWTAuthViewMixin(JWTCreateAPIView):
"""Mixin for authentication views"""
def post(self, request, *args, **kwargs):
"""Implement POST method"""
_locale = self._get_locale(request)
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_200_OK)
access_token = serializer.data.get('access_token')
refresh_token = serializer.data.get('refresh_token')
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)
# OAuth2
class BaseOAuth2ViewMixin(generics.GenericAPIView):
"""BaseMixin for classic auth views"""
def get_client_id(self, source) -> str:
"""Get application client id"""
qs = Application.objects.by_source(source=source)
if qs.exists():
return qs.first().client_id
else:
raise utils_exceptions.ServiceError(data={
'detail': _('Application is not found')})
def get_client_secret(self, source) -> str:
"""Get application client id"""
if source == Application.MOBILE:
qs = Application.objects.by_source(source=source)
if qs.exists:
return qs.first().client_secret
else:
raise utils_exceptions.ServiceError(data={
'detail': _('Not found an application with this source')})
class OAuth2ViewMixin(CsrfExemptMixin, OAuthLibMixin, BaseOAuth2ViewMixin):
"""Basic mixin for OAuth2 views"""
server_class = oauth2_settings.OAUTH2_SERVER_CLASS
validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS
oauthlib_backend_class = OAuthLibCore
def prepare_request_data(self, validated_data: dict) -> dict:
"""Preparing request data"""
source = validated_data.get('source')
# Set OAuth2 request parameters
_request_data = {
'client_id': self.get_client_id(source)
}
# Fill client secret parameter by platform
if validated_data.get('source') == Application.MOBILE:
_request_data['client_secret'] = self.get_client_secret(source)
# Fill token parameter if transfer
if validated_data.get('token'):
_request_data['token'] = validated_data.get('token')
if _request_data:
return _request_data
else:
raise utils_exceptions.ServiceError()
# Sign in via Facebook
class OAuth2SignUpView(OAuth2ViewMixin, JWTGenericViewMixin):
"""
Implements an endpoint to convert a provider token to an access token
The endpoint is used in the following flows:
* Authorization code
* Client credentials
"""
server_class = SocialTokenServer
oauthlib_backend_class = KeepRequestCore
permission_classes = (permissions.AllowAny, )
serializer_class = serializers.OAuth2Serialzier
def get_jwt_token(self, user: User,
oauth2_access_token: str,
oauth2_refresh_token: str):
"""Get JWT token"""
token = jwt_tokens.RefreshToken.for_user(user)
# Adding additional information about user to payload
token['user'] = user.get_user_info()
# Adding OAuth2 tokens to payloads
token['oauth2_fb'] = {'access_token': oauth2_access_token,
'refresh_token': oauth2_refresh_token}
return token
def post(self, request, *args, **kwargs):
_locale = self._get_locale(request)
try:
locale = self._check_locale(locale=_locale)
# Preparing request data
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
request_data = self.prepare_request_data(serializer.validated_data)
request_data.update({
'grant_type': settings.OAUTH2_SOCIAL_AUTH_GRANT_TYPE,
'backend': settings.OAUTH2_SOCIAL_AUTH_BACKEND_NAME
})
# Use the rest framework `.data` to fake the post body of the django request.
request._request.POST = request._request.POST.copy()
for key, value in request_data.items():
request._request.POST[key] = value
# OAuth2 authentication process
url, headers, body, oauth2_status = self.create_token_response(request._request)
body = json.loads(body)
# Get JWT token
if oauth2_status != status.HTTP_200_OK:
raise ValueError('status isn\'t 200')
# Get authenticated user
user = User.objects.by_oauth2_access_token(token=body.get('access_token'))\
.first()
# Create JWT token and put oauth2 token (access, refresh tokens) in payload
token = self.get_jwt_token(user=user,
oauth2_access_token=body.get('access_token'),
oauth2_refresh_token=body.get('refresh_token'))
access_token = str(token.access_token)
refresh_token = str(token)
response = Response(data={'access_token': access_token,
'refresh_token': refresh_token},
status=status.HTTP_200_OK)
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)
# JWT
# Sign in via username and password
class SignUpView(JWTCreateAPIView):
"""View for classic signup"""
permission_classes = (permissions.AllowAny, )
serializer_class = serializers.SignupSerializer
def post(self, request, *args, **kwargs):
"""Implement POST-method"""
_locale = self._get_locale(request)
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)
if settings.USE_CELERY:
tasks.send_confirm_signup_email.delay(serializer.instance.id)
else:
tasks.send_confirm_signup_email(serializer.instance.id)
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),
response=response)
class SignupFinishView(JWTGenericViewMixin):
"""View for confirmation signup"""
permission_classes = (permissions.AllowAny, )
def get(self, request, *args, **kwargs):
"""Implement GET-method"""
_locale = self._get_locale(request)
try:
locale = self._check_locale(locale=_locale)
uidb64 = kwargs.get('uid')
token = kwargs.get('token')
uid = force_text(urlsafe_base64_decode(uidb64))
user = User.objects.filter(pk=uid)
if user.exists():
if not gm_token_generator.check_token(user.first(), token):
raise utils_exceptions.NotValidTokenError()
response = Response(status=status.HTTP_200_OK)
else:
raise utils_exceptions.UserNotFoundError()
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),
response=response)
def get_success_url(self):
"""Return url to success page considering Mobile component."""
return reverse('mobile:transaction-mobile:success')
def get_fail_url(self, **kwargs):
"""Return url to fail page considering Mobile component."""
return reverse('mobile:transaction-mobile:fail')
# Login by username|email + password
class LoginByUsernameOrEmailView(JWTAuthViewMixin):
"""Login by email and password"""
permission_classes = (permissions.AllowAny,)
serializer_class = serializers.LoginByUsernameOrEmailSerializer
# Refresh access_token
class RefreshTokenView(JWTGenericViewMixin):
"""Refresh access_token"""
permission_classes = (permissions.IsAuthenticated,)
serializer_class = serializers.RefreshTokenSerializer
def post(self, request, *args, **kwargs):
_locale = self._get_locale(request)
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 = serializer.data.get('access_token')
refresh_token = serializer.data.get('refresh_token')
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
class LogoutView(JWTGenericViewMixin):
"""Logout user"""
serializer_class = serializers.LogoutSerializer
def create(self, request, *args, **kwargs):
"""Override create method"""
_locale = self._get_locale(request)
try:
locale = self._check_locale(locale=_locale)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
response = Response(status=status.HTTP_200_OK)
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),
response=response)