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

189 lines
6.0 KiB
Python

"""Common serializer for application authorization"""
from django.contrib.auth import password_validation as password_validators
from rest_framework import serializers
from rest_framework import validators as rest_validators
from django.contrib.auth import authenticate
from django.conf import settings
from account import models as account_models
from authorization.models import Application
from utils import exceptions as utils_exceptions
# JWT
from rest_framework_simplejwt.tokens import RefreshToken, SlidingToken, UntypedToken
JWT_SETTINGS = settings.SIMPLE_JWT
# Mixins
class BaseAuthSerializerMixin(serializers.Serializer):
"""Base authorization serializer mixin"""
source = serializers.ChoiceField(choices=Application.SOURCES)
class JWTBaseMixin(serializers.Serializer):
"""
Mixin for JWT authentication.
Uses in serializers when need give in response access and refresh token
"""
# RESPONSE
refresh_token = serializers.CharField(read_only=True)
access_token = serializers.CharField(read_only=True)
def get_token(self):
"""Create JWT token"""
user = self.instance
token = RefreshToken.for_user(user)
token['user'] = user.get_user_info()
return token
def to_representation(self, instance):
"""Override to_representation method"""
token = self.get_token()
setattr(instance, 'refresh_token', str(token))
setattr(instance, 'access_token', str(token.access_token))
return super().to_representation(instance)
class LoginSerializerMixin(BaseAuthSerializerMixin):
"""Mixin for login serializers"""
password = serializers.CharField(write_only=True)
class ClassicAuthSerializerMixin(BaseAuthSerializerMixin):
"""Classic authorization serializer mixin"""
password = serializers.CharField(write_only=True)
newsletter = serializers.BooleanField()
# Serializers
class SignupSerializer(JWTBaseMixin, serializers.ModelSerializer):
"""Signup serializer serializer mixin"""
# REQUEST
username = serializers.CharField(
validators=(rest_validators.UniqueValidator(queryset=account_models.User.objects.all()),),
write_only=True
)
password = serializers.CharField(write_only=True)
email = serializers.EmailField(write_only=True)
newsletter = serializers.BooleanField(write_only=True)
class Meta:
model = account_models.User
fields = (
'username', 'password', 'email', 'newsletter',
'access_token', 'refresh_token',
)
def validate_password(self, data):
"""Custom password validation"""
try:
password_validators.validate_password(password=data)
except serializers.ValidationError as e:
raise serializers.ValidationError(str(e))
else:
return data
def create(self, validated_data):
"""Override create method"""
obj = account_models.User.objects.make(
username=validated_data.get('username'),
password=validated_data.get('password'),
email=validated_data.get('email'),
newsletter=validated_data.get('newsletter')
)
return obj
class LoginByUsernameSerializer(JWTBaseMixin, serializers.ModelSerializer):
"""Serializer for login user by username and password"""
username = serializers.CharField(write_only=True)
password = serializers.CharField(write_only=True)
class Meta:
"""Meta-class"""
model = account_models.User
fields = (
'username', 'password', 'refresh_token', 'access_token'
)
def validate(self, attrs):
"""Override validate method"""
username = attrs.pop('username')
password = attrs.pop('password')
user = authenticate(username=username,
password=password)
if not user:
raise utils_exceptions.UserNotFoundError()
self.instance = user
return attrs
class LoginByEmailSerializer(JWTBaseMixin, serializers.ModelSerializer):
"""Serializer for login user"""
email = serializers.EmailField(write_only=True)
password = serializers.CharField(write_only=True)
class Meta:
"""Meta-class"""
model = account_models.User
fields = (
'email', 'password', 'refresh_token', 'access_token'
)
def validate(self, attrs):
"""Override validate method"""
email = attrs.pop('email')
password = attrs.pop('password')
try:
user = account_models.User.objects.get(email=email)
except account_models.User.DoesNotExist:
raise utils_exceptions.UserNotFoundError()
else:
user = authenticate(username=user.get_username(),
password=password)
if not user:
raise utils_exceptions.UserNotFoundError()
self.instance = user
return attrs
class RefreshTokenSerializer(serializers.Serializer):
"""Serializer for refresh token view"""
refresh_token = serializers.CharField()
access_token = serializers.CharField(read_only=True)
def validate(self, attrs):
"""Override validate method"""
token = RefreshToken(attrs['refresh_token'])
data = {'access_token': str(token.access_token)}
if JWT_SETTINGS.get('ROTATE_REFRESH_TOKENS'):
if JWT_SETTINGS.get('BLACKLIST_AFTER_ROTATION'):
try:
# Attempt to blacklist the given refresh token
token.blacklist()
except AttributeError:
# If blacklist app not installed, `blacklist` method will
# not be present
pass
token.set_jti()
token.set_exp()
data['refresh_token'] = str(token)
return data
# OAuth
class OAuth2Serialzier(BaseAuthSerializerMixin):
"""Serializer OAuth2 authorization"""
token = serializers.CharField(max_length=255)
class OAuth2LogoutSerializer(BaseAuthSerializerMixin):
"""Serializer for logout"""