version 0.0.5.3: added method to login user with username and password
This commit is contained in:
parent
d0c362c88d
commit
237d6d3125
|
|
@ -1,10 +1,67 @@
|
|||
"""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 account import models as account_models
|
||||
from authorization.models import Application
|
||||
|
||||
|
||||
class OAuth2Serialzier(serializers.Serializer):
|
||||
"""Serializer OAuth2 authorization"""
|
||||
|
||||
# Mixins
|
||||
class BaseAuthSerializerMixin(serializers.Serializer):
|
||||
"""Base authorization serializer mixin"""
|
||||
source = serializers.ChoiceField(choices=Application.SOURCES)
|
||||
|
||||
|
||||
# Classic
|
||||
class SignUpSerializer(BaseAuthSerializerMixin, serializers.ModelSerializer):
|
||||
"""Serializer for signing up user"""
|
||||
email = serializers.CharField(
|
||||
validators=(rest_validators.UniqueValidator(queryset=account_models.User.objects.all()), ),
|
||||
write_only=True
|
||||
)
|
||||
username = serializers.CharField(
|
||||
validators=(rest_validators.UniqueValidator(queryset=account_models.User.objects.all()), ),
|
||||
write_only=True
|
||||
)
|
||||
password = serializers.CharField(write_only=True)
|
||||
newsletter = serializers.BooleanField()
|
||||
|
||||
class Meta:
|
||||
"""Meta-class"""
|
||||
model = account_models.User
|
||||
fields = ('email', 'username', 'newsletter',
|
||||
'password', 'source')
|
||||
|
||||
def validate_password(self, data):
|
||||
"""Custom password validation"""
|
||||
try:
|
||||
password_validators.validate_password(password=data)
|
||||
except serializers.ValidationError as e:
|
||||
raise serializers.ValidationError(e)
|
||||
else:
|
||||
return data
|
||||
|
||||
def create(self, validated_data):
|
||||
"""Override create method"""
|
||||
obj = account_models.User.objects.make(**validated_data)
|
||||
return obj
|
||||
|
||||
|
||||
class LoginSerializer(BaseAuthSerializerMixin, serializers.ModelSerializer):
|
||||
"""Serializer for login user"""
|
||||
username = serializers.CharField(
|
||||
write_only=True
|
||||
)
|
||||
password = serializers.CharField(write_only=True)
|
||||
|
||||
class Meta:
|
||||
"""Meta-class"""
|
||||
model = account_models.User
|
||||
fields = ('username', 'password', 'source')
|
||||
|
||||
|
||||
# OAuth
|
||||
class OAuth2Serialzier(BaseAuthSerializerMixin):
|
||||
"""Serializer OAuth2 authorization"""
|
||||
token = serializers.CharField(max_length=255)
|
||||
|
|
|
|||
|
|
@ -1,81 +1 @@
|
|||
"""Serializers for application authorization"""
|
||||
from rest_framework import serializers
|
||||
from rest_framework.authentication import authenticate
|
||||
from rest_framework import validators as rest_validators
|
||||
from account import models as account_models
|
||||
from django.contrib.auth import password_validation as password_validators
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class AuthTokenClassicSerializer(serializers.Serializer):
|
||||
username = serializers.CharField(
|
||||
label=_('Username'),
|
||||
required=False
|
||||
)
|
||||
password = serializers.CharField(
|
||||
label=_("Password"),
|
||||
style={'input_type': 'password'},
|
||||
trim_whitespace=False
|
||||
)
|
||||
email = serializers.EmailField(
|
||||
label=_("Email"),
|
||||
required=False
|
||||
)
|
||||
|
||||
def validate(self, attrs):
|
||||
username = attrs.get('username')
|
||||
password = attrs.get('password')
|
||||
email = attrs.get('email')
|
||||
|
||||
if username and password:
|
||||
user = authenticate(request=self.context.get('request'),
|
||||
username=username, password=password)
|
||||
elif email and password:
|
||||
user = authenticate(request=self.context.get('request'),
|
||||
email=email, password=password)
|
||||
else:
|
||||
msg = _('Must include "phone" and "password".')
|
||||
raise serializers.ValidationError(msg, code='authorization')
|
||||
if user:
|
||||
# From Django 1.10 onwards the `authenticate` call simply
|
||||
# returns `None` for is_active=False users.
|
||||
# (Assuming the default `ModelBackend` authentication backend.)
|
||||
if not user.is_active:
|
||||
msg = _('User account is disabled.')
|
||||
raise serializers.ValidationError(msg, code='authorization')
|
||||
attrs['user'] = user
|
||||
return attrs
|
||||
|
||||
|
||||
class SignUpSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for signing up user"""
|
||||
email = serializers.CharField(
|
||||
validators=(rest_validators.UniqueValidator(queryset=account_models.User.objects.all()), ),
|
||||
write_only=True
|
||||
)
|
||||
username = serializers.CharField(
|
||||
validators=(rest_validators.UniqueValidator(queryset=account_models.User.objects.all()), ),
|
||||
write_only=True
|
||||
)
|
||||
password = serializers.CharField(write_only=True)
|
||||
newsletter = serializers.BooleanField()
|
||||
|
||||
class Meta:
|
||||
"""Meta-class"""
|
||||
model = account_models.User
|
||||
fields = ('email', 'username',
|
||||
'newsletter', 'password')
|
||||
|
||||
def validate_password(self, data):
|
||||
"""Custom password validation"""
|
||||
try:
|
||||
password_validators.validate_password(password=data)
|
||||
except serializers.ValidationError as e:
|
||||
raise serializers.ValidationError(e)
|
||||
else:
|
||||
return data
|
||||
|
||||
def create(self, validated_data):
|
||||
"""Override create method"""
|
||||
obj = account_models.User.objects.make(**validated_data)
|
||||
return obj
|
||||
|
|
|
|||
|
|
@ -30,13 +30,13 @@ urlpatterns_social_django = [
|
|||
|
||||
urlpatterns_rest_framework_social_oauth2 = [
|
||||
url(r'^authorize/?$', AuthorizationView.as_view(), name="authorize"),
|
||||
url(r'^token/?$', TokenView.as_view(), name="token"),
|
||||
url('', include('social_django.urls', namespace="social")),
|
||||
url(r'^invalidate-sessions/?$', invalidate_sessions, name="invalidate_sessions")
|
||||
]
|
||||
|
||||
urlpatterns_api = [
|
||||
path('social/signup/', views.SocialSignUpView.as_view(), name='signup'),
|
||||
path('social/signup/', views.SocialSignUpView.as_view(), name='signup-social'),
|
||||
path('login/', views.LoginView.as_view(), name='login-classic'),
|
||||
path('revoke-token/', views.RevokeTokenView.as_view(), name="revoke_token"),
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ 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 permissions
|
||||
from rest_framework.generics import GenericAPIView
|
||||
from rest_framework.generics import GenericAPIView, CreateAPIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework_social_oauth2.oauth2_backends import KeepRequestCore
|
||||
from rest_framework_social_oauth2.oauth2_endpoints import SocialTokenServer
|
||||
|
|
@ -28,7 +28,8 @@ class OAuth2ViewMixin(GenericAPIView):
|
|||
if qs.exists():
|
||||
return qs.first().client_id
|
||||
else:
|
||||
raise utils_exceptions.SerivceError(data=_('Application is not found'))
|
||||
raise utils_exceptions.SerivceError(data={
|
||||
'detail': _('Application is not found')})
|
||||
|
||||
def get_client_secret(self, source) -> str:
|
||||
"""Get application client id"""
|
||||
|
|
@ -37,30 +38,70 @@ class OAuth2ViewMixin(GenericAPIView):
|
|||
if qs.exists:
|
||||
return qs.first().client_secret
|
||||
else:
|
||||
raise utils_exceptions.SerivceError(
|
||||
data=_('Not found an application with this source'))
|
||||
raise utils_exceptions.SerivceError(data={
|
||||
'detail': _('Not found an application with this source')})
|
||||
|
||||
def prepare_request_data(self, request_data):
|
||||
def prepare_request_data(self, validated_data: dict) -> dict:
|
||||
"""Preparing request data"""
|
||||
serializer = self.get_serializer(data=request_data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
# Set attributes
|
||||
source = serializer.validated_data.get('source')
|
||||
token = serializer.validated_data.get('token')
|
||||
source = validated_data.get('source')
|
||||
# Set OAuth2 request parameters
|
||||
_request_data = {
|
||||
'grant_type': settings.OAUTH2_SOCIAL_AUTH_GRANT_TYPE,
|
||||
'backend': settings.OAUTH2_SOCIAL_AUTH_BACKEND_NAME,
|
||||
'token': token,
|
||||
'client_id': self.get_client_id(source)
|
||||
}
|
||||
# Fill client secret parameter by platform
|
||||
if source == Application.MOBILE:
|
||||
if validated_data.get('source') == Application.MOBILE:
|
||||
_request_data['client_secret'] = self.get_client_secret(source)
|
||||
return _request_data
|
||||
# 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.SerivceError(data={
|
||||
'detail': 'Unknown request data'})
|
||||
|
||||
|
||||
# Create your views here.
|
||||
class LoginView(CsrfExemptMixin, OAuthLibMixin,
|
||||
OAuth2ViewMixin, GenericAPIView):
|
||||
"""
|
||||
Implements an endpoint to provide access tokens
|
||||
|
||||
The endpoint is used in the following flows:
|
||||
|
||||
* Authorization code
|
||||
* Password
|
||||
* Client credentials
|
||||
"""
|
||||
server_class = oauth2_settings.OAUTH2_SERVER_CLASS
|
||||
validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS
|
||||
oauthlib_backend_class = OAuthLibCore
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
serializer_class = serializers.LoginSerializer
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
# 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': 'password',
|
||||
'username': serializer.validated_data.get('username'),
|
||||
'password': serializer.validated_data.get('password'),
|
||||
})
|
||||
# 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
|
||||
|
||||
url, headers, body, status = self.create_token_response(request._request)
|
||||
response = Response(data=json.loads(body), status=status)
|
||||
|
||||
for k, v in headers.items():
|
||||
response[k] = v
|
||||
return response
|
||||
|
||||
|
||||
class SocialSignUpView(CsrfExemptMixin, OAuthLibMixin,
|
||||
OAuth2ViewMixin, GenericAPIView):
|
||||
"""
|
||||
|
|
@ -80,7 +121,13 @@ class SocialSignUpView(CsrfExemptMixin, OAuthLibMixin,
|
|||
def post(self, request, *args, **kwargs):
|
||||
"""Override POST method"""
|
||||
# Preparing request data
|
||||
request_data = self.prepare_request_data(request_data=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():
|
||||
|
|
@ -108,7 +155,9 @@ class RevokeTokenView(CsrfExemptMixin, OAuthLibMixin,
|
|||
def post(self, request, *args, **kwargs):
|
||||
# Use the rest framework `.data` to fake the post body of the django request.
|
||||
# Preparing request data
|
||||
request_data = self.prepare_request_data(request_data=request.data)
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
request_data = self.prepare_request_data(serializer.validated_data)
|
||||
# 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():
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user