From b1db1f4d7b8aad67a94ed1e23bfd68c6760e773f Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Thu, 18 Jul 2019 16:17:44 -0300 Subject: [PATCH 01/15] Migration Login v1 to v2 and refact --- bothub/api/v1/views.py | 7 +++++ bothub/api/v2/account/__init__.py | 0 bothub/api/v2/account/serializers.py | 23 ++++++++++++++++ bothub/api/v2/account/views.py | 41 ++++++++++++++++++++++++++++ bothub/api/v2/metadata.py | 12 +++++++- bothub/api/v2/routers.py | 2 ++ 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 bothub/api/v2/account/__init__.py create mode 100644 bothub/api/v2/account/serializers.py create mode 100644 bothub/api/v2/account/views.py diff --git a/bothub/api/v1/views.py b/bothub/api/v1/views.py index 84311ce4..d72315b8 100644 --- a/bothub/api/v1/views.py +++ b/bothub/api/v1/views.py @@ -755,6 +755,13 @@ class RegisterUserViewSet( serializer_class = RegisterUserSerializer +@method_decorator( + name='create', + decorator=swagger_auto_schema( + responses={201: '{"token":"TOKEN"}'}, + deprecated=True + ), +) class LoginViewSet(GenericViewSet): """ diff --git a/bothub/api/v2/account/__init__.py b/bothub/api/v2/account/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bothub/api/v2/account/serializers.py b/bothub/api/v2/account/serializers.py new file mode 100644 index 00000000..e5710616 --- /dev/null +++ b/bothub/api/v2/account/serializers.py @@ -0,0 +1,23 @@ +from rest_framework.authtoken.serializers import AuthTokenSerializer +from rest_framework import serializers + +from bothub.authentication.models import User +from ..fields import PasswordField +from django.utils.translation import gettext as _ + + +class LoginSerializer(AuthTokenSerializer, serializers.ModelSerializer): + username = serializers.EmailField( + label=_('Email'), + ) + password = PasswordField( + label=_('Password'), + ) + + class Meta: + model = User + fields = [ + 'username', + 'password', + ] + ref_name = None diff --git a/bothub/api/v2/account/views.py b/bothub/api/v2/account/views.py new file mode 100644 index 00000000..a68a288c --- /dev/null +++ b/bothub/api/v2/account/views.py @@ -0,0 +1,41 @@ +from django.utils.decorators import method_decorator +from rest_framework.viewsets import GenericViewSet +from rest_framework.response import Response +from rest_framework.authtoken.models import Token +from rest_framework import status, mixins +from drf_yasg.utils import swagger_auto_schema +from bothub.api.v2.metadata import Metadata +from bothub.authentication.models import User + +from .serializers import LoginSerializer + + +@method_decorator( + name='create', + decorator=swagger_auto_schema( + responses={201: '{"token":"TOKEN"}'} + ) +) +class LoginViewSet(mixins.CreateModelMixin, GenericViewSet): + + """ + Login Users + """ + + queryset = User.objects + serializer_class = LoginSerializer + lookup_field = ('username', 'password') + metadata_class = Metadata + + def create(self, request, *args, **kwargs): + serializer = self.serializer_class( + data=request.data, + context={'request': request}) + serializer.is_valid(raise_exception=True) + user = serializer.validated_data['user'] + token, created = Token.objects.get_or_create(user=user) + return Response( + { + 'token': token.key, + }, + status.HTTP_201_CREATED if created else status.HTTP_200_OK) diff --git a/bothub/api/v2/metadata.py b/bothub/api/v2/metadata.py index 0677bc7e..6a0f707c 100644 --- a/bothub/api/v2/metadata.py +++ b/bothub/api/v2/metadata.py @@ -5,6 +5,11 @@ from rest_framework import serializers from rest_framework.utils.field_mapping import ClassLookupDict +from bothub.api.v2.fields import PasswordField +from bothub.api.v2.fields import ModelMultipleChoiceField +from bothub.api.v2.fields import TextField +from bothub.api.v2.fields import EntityText + class Metadata(BaseMetadata): label_lookup = ClassLookupDict({ @@ -30,6 +35,11 @@ class Metadata(BaseMetadata): serializers.DictField: 'nested object', serializers.Serializer: 'nested object', serializers.ManyRelatedField: 'multiple choice', + serializers.HiddenField: 'hidden', + PasswordField: 'password', + ModelMultipleChoiceField: 'multiple choice', + TextField: 'text', + EntityText: 'entity text', }) def determine_metadata(self, request, view): @@ -52,7 +62,7 @@ def determine_metadata(self, request, view): def determine_actions(self, request, view): actions = {} - for method in ['PUT', 'POST']: + for method in {'PUT', 'POST'} & set(view.allowed_methods): serializer = view.get_serializer() actions[method] = self.get_serializer_info(serializer) view.request = request diff --git a/bothub/api/v2/routers.py b/bothub/api/v2/routers.py index 680ee2c7..b8cac740 100644 --- a/bothub/api/v2/routers.py +++ b/bothub/api/v2/routers.py @@ -7,6 +7,7 @@ from .examples.views import ExamplesViewSet from .evaluate.views import EvaluateViewSet from .evaluate.views import ResultsListViewSet +from .account.views import LoginViewSet router = routers.SimpleRouter() @@ -17,3 +18,4 @@ router.register('examples', ExamplesViewSet) router.register('evaluate/results', ResultsListViewSet) router.register('evaluate', EvaluateViewSet) +router.register('account/login', LoginViewSet) From aebc23ec519fc60ce0d6df845af7b2891d0858ec Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Thu, 18 Jul 2019 16:26:50 -0300 Subject: [PATCH 02/15] Migration Register v1 to v2 and refact --- bothub/api/v1/views.py | 6 ++++++ bothub/api/v2/account/serializers.py | 26 +++++++++++++++++++++++++- bothub/api/v2/account/views.py | 13 +++++++++++++ bothub/api/v2/routers.py | 3 ++- 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/bothub/api/v1/views.py b/bothub/api/v1/views.py index d72315b8..db7b7d74 100644 --- a/bothub/api/v1/views.py +++ b/bothub/api/v1/views.py @@ -745,6 +745,12 @@ class RepositoryExamplesViewSet( ] +@method_decorator( + name='create', + decorator=swagger_auto_schema( + deprecated=True + ), +) class RegisterUserViewSet( mixins.CreateModelMixin, GenericViewSet): diff --git a/bothub/api/v2/account/serializers.py b/bothub/api/v2/account/serializers.py index e5710616..75f54483 100644 --- a/bothub/api/v2/account/serializers.py +++ b/bothub/api/v2/account/serializers.py @@ -1,9 +1,11 @@ +from django.utils.translation import gettext as _ +from django.contrib.auth.password_validation import validate_password +from django.contrib.auth.hashers import make_password from rest_framework.authtoken.serializers import AuthTokenSerializer from rest_framework import serializers from bothub.authentication.models import User from ..fields import PasswordField -from django.utils.translation import gettext as _ class LoginSerializer(AuthTokenSerializer, serializers.ModelSerializer): @@ -21,3 +23,25 @@ class Meta: 'password', ] ref_name = None + + +class RegisterUserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = [ + 'email', + 'name', + 'nickname', + 'password', + ] + ref_name = None + + password = PasswordField( + write_only=True, + validators=[ + validate_password, + ]) + + @staticmethod + def validate_password(value): + return make_password(value) diff --git a/bothub/api/v2/account/views.py b/bothub/api/v2/account/views.py index a68a288c..403d423d 100644 --- a/bothub/api/v2/account/views.py +++ b/bothub/api/v2/account/views.py @@ -8,6 +8,7 @@ from bothub.authentication.models import User from .serializers import LoginSerializer +from .serializers import RegisterUserSerializer @method_decorator( @@ -39,3 +40,15 @@ def create(self, request, *args, **kwargs): 'token': token.key, }, status.HTTP_201_CREATED if created else status.HTTP_200_OK) + + +class RegisterUserViewSet( + mixins.CreateModelMixin, + GenericViewSet): + """ + Register new user + """ + queryset = User.objects + serializer_class = RegisterUserSerializer + lookup_field = ('email', 'name', 'nickname', 'password') + metadata_class = Metadata diff --git a/bothub/api/v2/routers.py b/bothub/api/v2/routers.py index b8cac740..37d5dc5a 100644 --- a/bothub/api/v2/routers.py +++ b/bothub/api/v2/routers.py @@ -8,7 +8,7 @@ from .evaluate.views import EvaluateViewSet from .evaluate.views import ResultsListViewSet from .account.views import LoginViewSet - +from .account.views import RegisterUserViewSet router = routers.SimpleRouter() router.register('repository', RepositoryViewSet) @@ -19,3 +19,4 @@ router.register('evaluate/results', ResultsListViewSet) router.register('evaluate', EvaluateViewSet) router.register('account/login', LoginViewSet) +router.register('account/register', RegisterUserViewSet) From 4d144604636f729a3c10c1c13bd08dd59f1812ed Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Thu, 18 Jul 2019 17:41:18 -0300 Subject: [PATCH 03/15] Custom Routers v2 --- bothub/api/v2/evaluate/views.py | 2 - bothub/api/v2/repository/views.py | 2 +- bothub/api/v2/routers.py | 72 ++++++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/bothub/api/v2/evaluate/views.py b/bothub/api/v2/evaluate/views.py index 87e13e6d..6bb6b471 100644 --- a/bothub/api/v2/evaluate/views.py +++ b/bothub/api/v2/evaluate/views.py @@ -126,7 +126,6 @@ class EvaluateViewSet( """ Manager evaluate (tests). """ - lookup_fields = ('pk', 'repository_uuid') queryset = RepositoryEvaluate.objects serializer_class = RepositoryEvaluateSerializer permission_classes = [ @@ -196,7 +195,6 @@ class ResultsListViewSet( GenericViewSet): queryset = RepositoryEvaluateResult.objects - lookup_fields = ['repository_uuid'] serializer_class = RepositoryEvaluateResultVersionsSerializer permission_classes = [ IsAuthenticated, diff --git a/bothub/api/v2/repository/views.py b/bothub/api/v2/repository/views.py index c2d90f91..f4379cdc 100644 --- a/bothub/api/v2/repository/views.py +++ b/bothub/api/v2/repository/views.py @@ -70,7 +70,7 @@ class RepositoryVotesViewSet( """ queryset = RepositoryVote.objects.all() lookup_field = 'repository' - lookup_fields = ['user', 'repository'] + lookup_fields = ['repository'] serializer_class = RepositoryVotesSerializer permission_classes = [ IsAuthenticatedOrReadOnly diff --git a/bothub/api/v2/routers.py b/bothub/api/v2/routers.py index 37d5dc5a..f49d1aa0 100644 --- a/bothub/api/v2/routers.py +++ b/bothub/api/v2/routers.py @@ -10,7 +10,77 @@ from .account.views import LoginViewSet from .account.views import RegisterUserViewSet -router = routers.SimpleRouter() + +class Router(routers.SimpleRouter): + routes = [ + # Dynamically generated list routes. + # Generated using @action decorator + # on methods of the viewset. + routers.DynamicRoute( + url=r'^{prefix}/{url_path}{trailing_slash}$', + name='{basename}-{url_name}', + detail=True, + initkwargs={}, + ), + # Dynamically generated detail routes. + # Generated using @action decorator on methods of the viewset. + routers.DynamicRoute( + url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$', + name='{basename}-{url_name}', + detail=True, + initkwargs={}, + ), + ] + + def get_routes(self, viewset): + ret = super().get_routes(viewset) + lookup_field = getattr(viewset, 'lookup_field', None) + + if lookup_field: + # List route. + ret.append(routers.Route( + url=r'^{prefix}{trailing_slash}$', + mapping={ + 'get': 'list', + 'post': 'create' + }, + name='{basename}-list', + detail=False, + initkwargs={'suffix': 'List'}, + )) + + detail_url_regex = r'^{prefix}/{lookup}{trailing_slash}$' + if not lookup_field: + detail_url_regex = r'^{prefix}{trailing_slash}$' + # Detail route. + ret.append(routers.Route( + url=detail_url_regex, + mapping={ + 'get': 'retrieve', + 'put': 'update', + 'patch': 'partial_update', + 'delete': 'destroy' + }, + name='{basename}-detail', + detail=True, + initkwargs={'suffix': 'Instance'} + )) + + return ret + + def get_lookup_regex(self, viewset, lookup_prefix=''): + lookup_fields = getattr(viewset, 'lookup_fields', None) + if lookup_fields: + base_regex = '(?P<{lookup_prefix}{lookup_url_kwarg}>[^/.]+)' + return '/'.join(map( + lambda x: base_regex.format( + lookup_prefix=lookup_prefix, + lookup_url_kwarg=x), + lookup_fields)) + return super().get_lookup_regex(viewset, lookup_prefix) + + +router = Router() router.register('repository', RepositoryViewSet) router.register('repository-votes', RepositoryVotesViewSet) router.register('repositories', RepositoriesViewSet) From 5cad7f7607f717d6e5b1881632ce0362b8deb115 Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Fri, 19 Jul 2019 13:44:16 -0300 Subject: [PATCH 04/15] Migration Change Password v1 to v2 and refact --- bothub/api/v1/views.py | 6 ++++ bothub/api/v2/account/permissions.py | 9 ++++++ bothub/api/v2/account/serializers.py | 16 ++++++++++ bothub/api/v2/account/views.py | 45 +++++++++++++++++++++++++++- bothub/api/v2/routers.py | 2 ++ 5 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 bothub/api/v2/account/permissions.py diff --git a/bothub/api/v1/views.py b/bothub/api/v1/views.py index db7b7d74..c4776f00 100644 --- a/bothub/api/v1/views.py +++ b/bothub/api/v1/views.py @@ -791,6 +791,12 @@ def create(self, request, *args, **kwargs): status.HTTP_201_CREATED if created else status.HTTP_200_OK) +@method_decorator( + name='update', + decorator=swagger_auto_schema( + deprecated=True + ), +) class ChangePasswordViewSet(GenericViewSet): """ Change current user password. diff --git a/bothub/api/v2/account/permissions.py b/bothub/api/v2/account/permissions.py new file mode 100644 index 00000000..8a7ecf39 --- /dev/null +++ b/bothub/api/v2/account/permissions.py @@ -0,0 +1,9 @@ +from rest_framework import permissions + + +class ChangePasswordPermission(permissions.BasePermission): + + def has_permission(self, request, view): + if request.user.check_password(request.data.get('password')): + return False + return True diff --git a/bothub/api/v2/account/serializers.py b/bothub/api/v2/account/serializers.py index 75f54483..fe4f2f22 100644 --- a/bothub/api/v2/account/serializers.py +++ b/bothub/api/v2/account/serializers.py @@ -45,3 +45,19 @@ class Meta: @staticmethod def validate_password(value): return make_password(value) + + +class ChangePasswordSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = [ + 'current_password', + 'password' + ] + ref_name = None + current_password = PasswordField( + required=True + ) + password = PasswordField( + required=True + ) diff --git a/bothub/api/v2/account/views.py b/bothub/api/v2/account/views.py index 403d423d..1f816d00 100644 --- a/bothub/api/v2/account/views.py +++ b/bothub/api/v2/account/views.py @@ -2,13 +2,17 @@ from rest_framework.viewsets import GenericViewSet from rest_framework.response import Response from rest_framework.authtoken.models import Token -from rest_framework import status, mixins +from rest_framework import status, mixins, exceptions +from rest_framework import permissions from drf_yasg.utils import swagger_auto_schema + +from bothub.api.v2.account.permissions import ChangePasswordPermission from bothub.api.v2.metadata import Metadata from bothub.authentication.models import User from .serializers import LoginSerializer from .serializers import RegisterUserSerializer +from .serializers import ChangePasswordSerializer @method_decorator( @@ -52,3 +56,42 @@ class RegisterUserViewSet( serializer_class = RegisterUserSerializer lookup_field = ('email', 'name', 'nickname', 'password') metadata_class = Metadata + + +class ChangePasswordViewSet(mixins.UpdateModelMixin, GenericViewSet): + """ + Change current user password. + """ + serializer_class = ChangePasswordSerializer + queryset = User.objects + lookup_field = None + permission_classes = [ + permissions.IsAuthenticated, + ChangePasswordPermission + ] + metadata_class = Metadata + + def permission_denied(self, request, message=None): + raise exceptions.PermissionDenied(detail='Wrong password') + + def get_object(self, *args, **kwargs): + request = self.request + user = request.user + + # May raise a permission denied + self.check_object_permissions(self.request, user) + + return user + + def update(self, request, *args, **kwargs): + self.object = self.get_object() + serializer = self.get_serializer(data=request.data) + + if serializer.is_valid(): + self.object.set_password(serializer.data.get('password')) + self.object.save() + return Response({}, status=status.HTTP_200_OK) + + return Response( + serializer.errors, + status=status.HTTP_400_BAD_REQUEST) diff --git a/bothub/api/v2/routers.py b/bothub/api/v2/routers.py index f49d1aa0..ecdb7288 100644 --- a/bothub/api/v2/routers.py +++ b/bothub/api/v2/routers.py @@ -9,6 +9,7 @@ from .evaluate.views import ResultsListViewSet from .account.views import LoginViewSet from .account.views import RegisterUserViewSet +from .account.views import ChangePasswordViewSet class Router(routers.SimpleRouter): @@ -90,3 +91,4 @@ def get_lookup_regex(self, viewset, lookup_prefix=''): router.register('evaluate', EvaluateViewSet) router.register('account/login', LoginViewSet) router.register('account/register', RegisterUserViewSet) +router.register('account/change-password', ChangePasswordViewSet) From 328c2b0aeff10335dd3334b6bcfa402cab83b43d Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Fri, 19 Jul 2019 14:11:35 -0300 Subject: [PATCH 05/15] Migration Forgot-Password v1 to v2 and refact --- bothub/api/v1/views.py | 6 +++++ bothub/api/v2/account/permissions.py | 9 +++++++ bothub/api/v2/account/serializers.py | 35 ++++++++++++++++++++-------- bothub/api/v2/account/views.py | 35 ++++++++++++++++++++++++++++ bothub/api/v2/routers.py | 2 ++ 5 files changed, 77 insertions(+), 10 deletions(-) diff --git a/bothub/api/v1/views.py b/bothub/api/v1/views.py index c4776f00..5c43d2c2 100644 --- a/bothub/api/v1/views.py +++ b/bothub/api/v1/views.py @@ -831,6 +831,12 @@ def update(self, request, *args, **kwargs): status=status.HTTP_400_BAD_REQUEST) +@method_decorator( + name='create', + decorator=swagger_auto_schema( + deprecated=True + ), +) class RequestResetPassword(GenericViewSet): """ Request reset password diff --git a/bothub/api/v2/account/permissions.py b/bothub/api/v2/account/permissions.py index 8a7ecf39..2cbee900 100644 --- a/bothub/api/v2/account/permissions.py +++ b/bothub/api/v2/account/permissions.py @@ -1,4 +1,5 @@ from rest_framework import permissions +from bothub.authentication.models import User class ChangePasswordPermission(permissions.BasePermission): @@ -7,3 +8,11 @@ def has_permission(self, request, view): if request.user.check_password(request.data.get('password')): return False return True + + +class RequestResetPasswordPermission(permissions.BasePermission): + + def has_permission(self, request, view): + if User.objects.filter(email=request.data.get('email')).count() == 0: + return False + return True diff --git a/bothub/api/v2/account/serializers.py b/bothub/api/v2/account/serializers.py index fe4f2f22..1731fd85 100644 --- a/bothub/api/v2/account/serializers.py +++ b/bothub/api/v2/account/serializers.py @@ -26,6 +26,12 @@ class Meta: class RegisterUserSerializer(serializers.ModelSerializer): + password = PasswordField( + write_only=True, + validators=[ + validate_password, + ]) + class Meta: model = User fields = [ @@ -36,18 +42,19 @@ class Meta: ] ref_name = None - password = PasswordField( - write_only=True, - validators=[ - validate_password, - ]) - @staticmethod def validate_password(value): return make_password(value) class ChangePasswordSerializer(serializers.ModelSerializer): + current_password = PasswordField( + required=True + ) + password = PasswordField( + required=True + ) + class Meta: model = User fields = [ @@ -55,9 +62,17 @@ class Meta: 'password' ] ref_name = None - current_password = PasswordField( - required=True - ) - password = PasswordField( + + +class RequestResetPasswordSerializer(serializers.ModelSerializer): + email = serializers.EmailField( + label=_('Email'), required=True ) + + class Meta: + model = User + fields = [ + 'email' + ] + ref_name = None diff --git a/bothub/api/v2/account/views.py b/bothub/api/v2/account/views.py index 1f816d00..44d6cf84 100644 --- a/bothub/api/v2/account/views.py +++ b/bothub/api/v2/account/views.py @@ -7,12 +7,14 @@ from drf_yasg.utils import swagger_auto_schema from bothub.api.v2.account.permissions import ChangePasswordPermission +from bothub.api.v2.account.permissions import RequestResetPasswordPermission from bothub.api.v2.metadata import Metadata from bothub.authentication.models import User from .serializers import LoginSerializer from .serializers import RegisterUserSerializer from .serializers import ChangePasswordSerializer +from .serializers import RequestResetPasswordSerializer @method_decorator( @@ -95,3 +97,36 @@ def update(self, request, *args, **kwargs): return Response( serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class RequestResetPasswordViewSet(mixins.CreateModelMixin, GenericViewSet): + """ + Request reset password + """ + serializer_class = RequestResetPasswordSerializer + queryset = User.objects + lookup_field = ['email'] + permission_classes = [ + permissions.AllowAny, + RequestResetPasswordPermission + ] + metadata_class = Metadata + + def permission_denied(self, request, message=None): + raise exceptions.PermissionDenied( + detail='No user registered with this email' + ) + + def get_object(self): + return self.queryset.get(email=self.request.data.get('email')) + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + if serializer.is_valid(): + self.object = self.get_object() + self.object.send_reset_password_email() + return Response({}) + return Response( + serializer.errors, + status=status.HTTP_400_BAD_REQUEST) + diff --git a/bothub/api/v2/routers.py b/bothub/api/v2/routers.py index ecdb7288..b815992c 100644 --- a/bothub/api/v2/routers.py +++ b/bothub/api/v2/routers.py @@ -10,6 +10,7 @@ from .account.views import LoginViewSet from .account.views import RegisterUserViewSet from .account.views import ChangePasswordViewSet +from .account.views import RequestResetPasswordViewSet class Router(routers.SimpleRouter): @@ -92,3 +93,4 @@ def get_lookup_regex(self, viewset, lookup_prefix=''): router.register('account/login', LoginViewSet) router.register('account/register', RegisterUserViewSet) router.register('account/change-password', ChangePasswordViewSet) +router.register('account/forgot-password', RequestResetPasswordViewSet) From 9d789f45e54ce60a4ef27c2ca4e78bae26d3a82f Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Sat, 20 Jul 2019 10:47:56 -0300 Subject: [PATCH 06/15] [fix] Permissions v2 --- bothub/api/v2/account/permissions.py | 18 ------------------ bothub/api/v2/account/serializers.py | 19 ++++++++++++++++++- bothub/api/v2/account/views.py | 21 +++------------------ 3 files changed, 21 insertions(+), 37 deletions(-) delete mode 100644 bothub/api/v2/account/permissions.py diff --git a/bothub/api/v2/account/permissions.py b/bothub/api/v2/account/permissions.py deleted file mode 100644 index 2cbee900..00000000 --- a/bothub/api/v2/account/permissions.py +++ /dev/null @@ -1,18 +0,0 @@ -from rest_framework import permissions -from bothub.authentication.models import User - - -class ChangePasswordPermission(permissions.BasePermission): - - def has_permission(self, request, view): - if request.user.check_password(request.data.get('password')): - return False - return True - - -class RequestResetPasswordPermission(permissions.BasePermission): - - def has_permission(self, request, view): - if User.objects.filter(email=request.data.get('email')).count() == 0: - return False - return True diff --git a/bothub/api/v2/account/serializers.py b/bothub/api/v2/account/serializers.py index 1731fd85..d04c2739 100644 --- a/bothub/api/v2/account/serializers.py +++ b/bothub/api/v2/account/serializers.py @@ -3,6 +3,7 @@ from django.contrib.auth.hashers import make_password from rest_framework.authtoken.serializers import AuthTokenSerializer from rest_framework import serializers +from rest_framework.exceptions import ValidationError from bothub.authentication.models import User from ..fields import PasswordField @@ -52,7 +53,10 @@ class ChangePasswordSerializer(serializers.ModelSerializer): required=True ) password = PasswordField( - required=True + required=True, + validators=[ + validate_password, + ] ) class Meta: @@ -63,6 +67,12 @@ class Meta: ] ref_name = None + def validate_current_password(self, value): + request = self.context.get('request') + if not request.user.check_password(value): + raise ValidationError(_('Wrong password')) + return value + class RequestResetPasswordSerializer(serializers.ModelSerializer): email = serializers.EmailField( @@ -76,3 +86,10 @@ class Meta: 'email' ] ref_name = None + + def validate_email(self, value): + try: + User.objects.get(email=value) + return value + except User.DoesNotExist: + raise ValidationError(_('No user registered with this email')) diff --git a/bothub/api/v2/account/views.py b/bothub/api/v2/account/views.py index 44d6cf84..0753d47c 100644 --- a/bothub/api/v2/account/views.py +++ b/bothub/api/v2/account/views.py @@ -2,12 +2,9 @@ from rest_framework.viewsets import GenericViewSet from rest_framework.response import Response from rest_framework.authtoken.models import Token -from rest_framework import status, mixins, exceptions +from rest_framework import status, mixins from rest_framework import permissions from drf_yasg.utils import swagger_auto_schema - -from bothub.api.v2.account.permissions import ChangePasswordPermission -from bothub.api.v2.account.permissions import RequestResetPasswordPermission from bothub.api.v2.metadata import Metadata from bothub.authentication.models import User @@ -69,13 +66,10 @@ class ChangePasswordViewSet(mixins.UpdateModelMixin, GenericViewSet): lookup_field = None permission_classes = [ permissions.IsAuthenticated, - ChangePasswordPermission + # ChangePasswordPermission ] metadata_class = Metadata - def permission_denied(self, request, message=None): - raise exceptions.PermissionDenied(detail='Wrong password') - def get_object(self, *args, **kwargs): request = self.request user = request.user @@ -106,17 +100,9 @@ class RequestResetPasswordViewSet(mixins.CreateModelMixin, GenericViewSet): serializer_class = RequestResetPasswordSerializer queryset = User.objects lookup_field = ['email'] - permission_classes = [ - permissions.AllowAny, - RequestResetPasswordPermission - ] + permission_classes = [permissions.AllowAny] metadata_class = Metadata - def permission_denied(self, request, message=None): - raise exceptions.PermissionDenied( - detail='No user registered with this email' - ) - def get_object(self): return self.queryset.get(email=self.request.data.get('email')) @@ -129,4 +115,3 @@ def create(self, request, *args, **kwargs): return Response( serializer.errors, status=status.HTTP_400_BAD_REQUEST) - From 6e811908a9f5411d98a4b943a8392c1f07d4f086 Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Sat, 20 Jul 2019 18:03:44 -0300 Subject: [PATCH 07/15] Migration User-Profile v1 to v2 and refact --- bothub/api/v1/views.py | 24 ++++++++++++++++++++++++ bothub/api/v2/account/serializers.py | 11 +++++++++++ bothub/api/v2/account/views.py | 16 ++++++++++++++++ bothub/api/v2/routers.py | 2 ++ 4 files changed, 53 insertions(+) diff --git a/bothub/api/v1/views.py b/bothub/api/v1/views.py index 5c43d2c2..fb39479f 100644 --- a/bothub/api/v1/views.py +++ b/bothub/api/v1/views.py @@ -878,6 +878,24 @@ def update(self, request, *args, **kwargs): status=status.HTTP_400_BAD_REQUEST) +@method_decorator( + name='update', + decorator=swagger_auto_schema( + deprecated=True + ), +) +@method_decorator( + name='retrieve', + decorator=swagger_auto_schema( + deprecated=True + ), +) +@method_decorator( + name='partial_update', + decorator=swagger_auto_schema( + deprecated=True + ), +) class MyUserProfileViewSet( mixins.RetrieveModelMixin, mixins.UpdateModelMixin, @@ -911,6 +929,12 @@ def get_object(self, *args, **kwargs): return user +@method_decorator( + name='retrieve', + decorator=swagger_auto_schema( + deprecated=True + ), +) class UserProfileViewSet( mixins.RetrieveModelMixin, GenericViewSet): diff --git a/bothub/api/v2/account/serializers.py b/bothub/api/v2/account/serializers.py index d04c2739..25db3730 100644 --- a/bothub/api/v2/account/serializers.py +++ b/bothub/api/v2/account/serializers.py @@ -93,3 +93,14 @@ def validate_email(self, value): return value except User.DoesNotExist: raise ValidationError(_('No user registered with this email')) + + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = [ + 'nickname', + 'name', + 'locale', + ] + ref_name = None diff --git a/bothub/api/v2/account/views.py b/bothub/api/v2/account/views.py index 0753d47c..ed2f4f55 100644 --- a/bothub/api/v2/account/views.py +++ b/bothub/api/v2/account/views.py @@ -12,6 +12,7 @@ from .serializers import RegisterUserSerializer from .serializers import ChangePasswordSerializer from .serializers import RequestResetPasswordSerializer +from .serializers import UserSerializer @method_decorator( @@ -115,3 +116,18 @@ def create(self, request, *args, **kwargs): return Response( serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class UserProfileViewSet( + mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + GenericViewSet): + """ + Get user profile + """ + serializer_class = UserSerializer + queryset = User.objects + lookup_field = 'nickname' + permission_classes = [ + permissions.IsAuthenticatedOrReadOnly + ] diff --git a/bothub/api/v2/routers.py b/bothub/api/v2/routers.py index b815992c..1a62b485 100644 --- a/bothub/api/v2/routers.py +++ b/bothub/api/v2/routers.py @@ -11,6 +11,7 @@ from .account.views import RegisterUserViewSet from .account.views import ChangePasswordViewSet from .account.views import RequestResetPasswordViewSet +from .account.views import UserProfileViewSet class Router(routers.SimpleRouter): @@ -94,3 +95,4 @@ def get_lookup_regex(self, viewset, lookup_prefix=''): router.register('account/register', RegisterUserViewSet) router.register('account/change-password', ChangePasswordViewSet) router.register('account/forgot-password', RequestResetPasswordViewSet) +router.register('account/user-profile', UserProfileViewSet) From 83f0c7de757d0a6d5daa03ba7413cf53adeadc47 Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Sat, 20 Jul 2019 18:13:43 -0300 Subject: [PATCH 08/15] Migrate Search-User v1 to v2 and refact --- bothub/api/v1/views.py | 6 ++++++ bothub/api/v2/account/views.py | 29 +++++++++++++++++++++++++++++ bothub/api/v2/routers.py | 2 ++ 3 files changed, 37 insertions(+) diff --git a/bothub/api/v1/views.py b/bothub/api/v1/views.py index fb39479f..6f01fcbe 100644 --- a/bothub/api/v1/views.py +++ b/bothub/api/v1/views.py @@ -1082,6 +1082,12 @@ def update(self, *args, **kwargs): return response +@method_decorator( + name='list', + decorator=swagger_auto_schema( + deprecated=True, + ) +) class SearchUserViewSet( mixins.ListModelMixin, GenericViewSet): diff --git a/bothub/api/v2/account/views.py b/bothub/api/v2/account/views.py index ed2f4f55..460e6822 100644 --- a/bothub/api/v2/account/views.py +++ b/bothub/api/v2/account/views.py @@ -1,4 +1,6 @@ from django.utils.decorators import method_decorator +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework.filters import SearchFilter from rest_framework.viewsets import GenericViewSet from rest_framework.response import Response from rest_framework.authtoken.models import Token @@ -131,3 +133,30 @@ class UserProfileViewSet( permission_classes = [ permissions.IsAuthenticatedOrReadOnly ] + + +class SearchUserViewSet( + mixins.ListModelMixin, + GenericViewSet): + serializer_class = UserSerializer + queryset = User.objects.all() + filter_backends = [ + DjangoFilterBackend, + SearchFilter, + ] + search_fields = [ + '=name', + '^name', + '$name', + '=nickname', + '^nickname', + '$nickname', + '=email', + ] + pagination_class = None + limit = 5 + + def list(self, request, *args, **kwargs): + queryset = self.filter_queryset(self.get_queryset())[:self.limit] + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) diff --git a/bothub/api/v2/routers.py b/bothub/api/v2/routers.py index 1a62b485..cb47cf50 100644 --- a/bothub/api/v2/routers.py +++ b/bothub/api/v2/routers.py @@ -12,6 +12,7 @@ from .account.views import ChangePasswordViewSet from .account.views import RequestResetPasswordViewSet from .account.views import UserProfileViewSet +from .account.views import SearchUserViewSet class Router(routers.SimpleRouter): @@ -96,3 +97,4 @@ def get_lookup_regex(self, viewset, lookup_prefix=''): router.register('account/change-password', ChangePasswordViewSet) router.register('account/forgot-password', RequestResetPasswordViewSet) router.register('account/user-profile', UserProfileViewSet) +router.register('account/search-user', SearchUserViewSet) From fb22dc267e631f87532541440cdc1bf9e7bb00f0 Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Sat, 20 Jul 2019 18:22:09 -0300 Subject: [PATCH 09/15] Migration Reset-Password v1 to v2 and refact --- bothub/api/v2/account/serializers.py | 27 +++++++++++++++++++++++++++ bothub/api/v2/account/views.py | 25 ++++++++++++++++++++++--- bothub/api/v2/routers.py | 2 ++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/bothub/api/v2/account/serializers.py b/bothub/api/v2/account/serializers.py index 25db3730..8b458ce6 100644 --- a/bothub/api/v2/account/serializers.py +++ b/bothub/api/v2/account/serializers.py @@ -104,3 +104,30 @@ class Meta: 'locale', ] ref_name = None + + +class ResetPasswordSerializer(serializers.ModelSerializer): + token = serializers.CharField( + label=_('Token'), + style={'show': False} + ) + password = PasswordField( + label=_('New Password'), + required=True, + validators=[ + validate_password, + ]) + + class Meta: + model = User + fields = [ + 'token', + 'password', + ] + ref_name = None + + def validate_token(self, value): + user = self.context.get('view').get_object() + if not user.check_password_reset_token(value): + raise ValidationError(_('Invalid token for this user')) + return value diff --git a/bothub/api/v2/account/views.py b/bothub/api/v2/account/views.py index 460e6822..79a12871 100644 --- a/bothub/api/v2/account/views.py +++ b/bothub/api/v2/account/views.py @@ -15,6 +15,7 @@ from .serializers import ChangePasswordSerializer from .serializers import RequestResetPasswordSerializer from .serializers import UserSerializer +from .serializers import ResetPasswordSerializer @method_decorator( @@ -135,9 +136,7 @@ class UserProfileViewSet( ] -class SearchUserViewSet( - mixins.ListModelMixin, - GenericViewSet): +class SearchUserViewSet(mixins.ListModelMixin, GenericViewSet): serializer_class = UserSerializer queryset = User.objects.all() filter_backends = [ @@ -160,3 +159,23 @@ def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset())[:self.limit] serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) + + +class ResetPasswordViewSet(GenericViewSet): + """ + Reset password + """ + serializer_class = ResetPasswordSerializer + queryset = User.objects + lookup_field = 'nickname' + + def update(self, request, *args, **kwargs): + self.object = self.get_object() + serializer = self.get_serializer(data=request.data) + if serializer.is_valid(): + self.object.set_password(serializer.data.get('password')) + self.object.save() + return Response({}) + return Response( + serializer.errors, + status=status.HTTP_400_BAD_REQUEST) diff --git a/bothub/api/v2/routers.py b/bothub/api/v2/routers.py index cb47cf50..80147f87 100644 --- a/bothub/api/v2/routers.py +++ b/bothub/api/v2/routers.py @@ -13,6 +13,7 @@ from .account.views import RequestResetPasswordViewSet from .account.views import UserProfileViewSet from .account.views import SearchUserViewSet +from .account.views import ResetPasswordViewSet class Router(routers.SimpleRouter): @@ -98,3 +99,4 @@ def get_lookup_regex(self, viewset, lookup_prefix=''): router.register('account/forgot-password', RequestResetPasswordViewSet) router.register('account/user-profile', UserProfileViewSet) router.register('account/search-user', SearchUserViewSet) +router.register('account/reset-password', ResetPasswordViewSet) From 34683a7dde3ce95def6655177964077ffd8058fb Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Sat, 20 Jul 2019 18:24:11 -0300 Subject: [PATCH 10/15] Update Reset-Password --- bothub/api/v1/views.py | 6 ++++++ bothub/api/v2/account/views.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/bothub/api/v1/views.py b/bothub/api/v1/views.py index 6f01fcbe..8f15eb62 100644 --- a/bothub/api/v1/views.py +++ b/bothub/api/v1/views.py @@ -858,6 +858,12 @@ def create(self, request, *args, **kwargs): status=status.HTTP_400_BAD_REQUEST) +@method_decorator( + name='update', + decorator=swagger_auto_schema( + deprecated=True + ), +) class ResetPassword(GenericViewSet): """ Reset password diff --git a/bothub/api/v2/account/views.py b/bothub/api/v2/account/views.py index 79a12871..f2b71327 100644 --- a/bothub/api/v2/account/views.py +++ b/bothub/api/v2/account/views.py @@ -161,7 +161,7 @@ def list(self, request, *args, **kwargs): return Response(serializer.data) -class ResetPasswordViewSet(GenericViewSet): +class ResetPasswordViewSet(mixins.UpdateModelMixin, GenericViewSet): """ Reset password """ From e4d7bcba024b935e5235d04b34cf74e6fba5c8aa Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Sat, 20 Jul 2019 18:45:32 -0300 Subject: [PATCH 11/15] Account tests --- bothub/api/v2/account/views.py | 3 +- bothub/api/v2/tests/test_account.py | 347 ++++++++++++++++++++++++++++ 2 files changed, 348 insertions(+), 2 deletions(-) create mode 100644 bothub/api/v2/tests/test_account.py diff --git a/bothub/api/v2/account/views.py b/bothub/api/v2/account/views.py index f2b71327..3cec556a 100644 --- a/bothub/api/v2/account/views.py +++ b/bothub/api/v2/account/views.py @@ -69,8 +69,7 @@ class ChangePasswordViewSet(mixins.UpdateModelMixin, GenericViewSet): queryset = User.objects lookup_field = None permission_classes = [ - permissions.IsAuthenticated, - # ChangePasswordPermission + permissions.IsAuthenticated ] metadata_class = Metadata diff --git a/bothub/api/v2/tests/test_account.py b/bothub/api/v2/tests/test_account.py new file mode 100644 index 00000000..bbb12021 --- /dev/null +++ b/bothub/api/v2/tests/test_account.py @@ -0,0 +1,347 @@ +import json + +from django.test import TestCase +from django.test import RequestFactory +from django.test.client import MULTIPART_CONTENT +from rest_framework import status + +from bothub.authentication.models import User + +from ..account.views import RegisterUserViewSet +from ..account.views import LoginViewSet +from ..account.views import ChangePasswordViewSet +from ..account.views import RequestResetPasswordViewSet +from ..account.views import ResetPasswordViewSet +from ..account.views import UserProfileViewSet + +from .utils import create_user_and_token + + +class LoginTestCase(TestCase): + def setUp(self): + self.factory = RequestFactory() + + self.password = 'abcgq!!123' + self.email = 'user@user.com' + + user = User.objects.create( + email=self.email, + nickname='user', + name='User') + user.set_password(self.password) + user.save(update_fields=['password']) + + def request(self, data): + request = self.factory.post( + '/v2/account/login/', + data) + response = LoginViewSet.as_view( + {'post': 'create'})(request) + response.render() + content_data = json.loads(response.content) + return (response, content_data,) + + def test_okay(self): + response, content_data = self.request({ + 'username': self.email, + 'password': self.password, + }) + self.assertEqual( + response.status_code, + status.HTTP_201_CREATED) + self.assertIn( + 'token', + content_data.keys()) + + def test_wrong_password(self): + response, content_data = self.request({ + 'username': self.email, + 'password': 'wrong', + }) + self.assertEqual( + response.status_code, + status.HTTP_400_BAD_REQUEST) + + +class RegisterUserTestCase(TestCase): + def setUp(self): + self.factory = RequestFactory() + + def request(self, data): + request = self.factory.post( + '/v2/account/register/', + data) + response = RegisterUserViewSet.as_view( + {'post': 'create'})(request) + response.render() + content_data = json.loads(response.content) + return (response, content_data,) + + def test_okay(self): + email = 'fake@user.com' + password = 'abc!1234' + response, content_data = self.request({ + 'email': email, + 'name': 'Fake', + 'nickname': 'fake', + 'password': password, + }) + self.assertEqual( + response.status_code, + status.HTTP_201_CREATED) + user = User.objects.get(email=email) + self.assertTrue(user.check_password(password)) + + def test_invalid_password(self): + response, content_data = self.request({ + 'email': 'fake@user.com', + 'name': 'Fake', + 'nickname': 'fake', + 'password': 'abc', + }) + self.assertEqual( + response.status_code, + status.HTTP_400_BAD_REQUEST) + self.assertIn( + 'password', + content_data.keys()) + + def test_unique_nickname(self): + nickname = 'fake' + User.objects.create_user('user1@user.com', nickname) + response, content_data = self.request({ + 'email': 'user2@user.com', + 'name': 'Fake', + 'nickname': nickname, + 'password': 'abc!1234', + }) + self.assertEqual( + response.status_code, + status.HTTP_400_BAD_REQUEST) + self.assertIn( + 'nickname', + content_data.keys()) + + def test_invalid_nickname_url_conflict(self): + URL_PATHS = [ + 'api', + 'docs', + 'admin', + ] + + for url_path in URL_PATHS: + response, content_data = self.request({ + 'email': '{}@fake.com'.format(url_path), + 'name': 'Fake', + 'nickname': url_path, + 'password': 'abc!1234', + }) + + self.assertEqual( + response.status_code, + status.HTTP_400_BAD_REQUEST) + self.assertIn( + 'nickname', + content_data.keys()) + + +class RequestResetPasswordTestCase(TestCase): + def setUp(self): + self.factory = RequestFactory() + + self.email = 'user@user.com' + + User.objects.create( + email=self.email, + nickname='user', + name='User') + + def request(self, data): + request = self.factory.post( + '/v2/account/forgot-password/', + data) + response = RequestResetPasswordViewSet.as_view( + {'post': 'create'})(request) + response.render() + content_data = json.loads(response.content) + return (response, content_data,) + + def test_okay(self): + response, content_data = self.request({ + 'email': self.email, + }) + + def test_email_not_found(self): + response, content_data = self.request({ + 'email': 'nouser@fake.com', + }) + self.assertEqual( + response.status_code, + status.HTTP_400_BAD_REQUEST) + self.assertIn( + 'email', + content_data.keys()) + + +class ResetPasswordTestCase(TestCase): + def setUp(self): + self.factory = RequestFactory() + + self.user = User.objects.create( + email='user@user.com', + nickname='user', + name='User') + self.reset_password_token = self.user.make_password_reset_token() + + def request(self, nickname, data): + request = self.factory.post( + '/v2/account/reset-password/{}/'.format(nickname), + data) + response = ResetPasswordViewSet.as_view( + {'post': 'update'})(request, nickname=nickname) + response.render() + content_data = json.loads(response.content) + return (response, content_data,) + + def test_okay(self): + new_password = 'valid12!' + response, content_data = self.request( + self.user.nickname, + { + 'token': self.reset_password_token, + 'password': new_password, + }) + self.assertEqual( + response.status_code, + status.HTTP_200_OK) + self.user = User.objects.get(pk=self.user.pk) + self.assertTrue(self.user.check_password(new_password)) + + def test_invalid_token(self): + response, content_data = self.request( + self.user.nickname, + { + 'token': '112233', + 'password': 'valid12!', + }) + self.assertEqual( + response.status_code, + status.HTTP_400_BAD_REQUEST) + self.assertIn( + 'token', + content_data.keys()) + + +class ChangePasswordTestCase(TestCase): + def setUp(self): + self.factory = RequestFactory() + self.user, self.user_token = create_user_and_token() + self.password = '12555q!66' + self.user.set_password(self.password) + self.user.save(update_fields=['password']) + + def request(self, data, token): + authorization_header = { + 'HTTP_AUTHORIZATION': 'Token {}'.format(token.key), + } + request = self.factory.post( + '/v2/account/change-password/', + data, + **authorization_header) + response = ChangePasswordViewSet.as_view( + {'post': 'update'})(request) + response.render() + content_data = json.loads(response.content) + return (response, content_data,) + + def test_okay(self): + new_password = 'kkl8&!qq' + response, content_data = self.request( + { + 'current_password': self.password, + 'password': new_password, + }, + self.user_token) + self.assertEqual( + response.status_code, + status.HTTP_200_OK) + + def test_wrong_password(self): + response, content_data = self.request( + { + 'current_password': 'wrong_password', + 'password': 'new_password', + }, + self.user_token) + self.assertEqual( + response.status_code, + status.HTTP_400_BAD_REQUEST) + self.assertIn( + 'current_password', + content_data.keys()) + + +class ListUserProfileTestCase(TestCase): + def setUp(self): + self.factory = RequestFactory() + self.user, self.user_token = create_user_and_token() + + def request(self, token): + request = self.factory.get( + '/v2/account/user-profile/{}/'.format(self.user.nickname) + ) + response = UserProfileViewSet.as_view( + {'get': 'retrieve'})(request) + response.render() + content_data = json.loads(response.content) + return (response, content_data,) + + def test_okay(self): + response, content_data = self.request(self.user_token) + self.assertEqual( + response.status_code, + status.HTTP_200_OK) + self.assertEqual( + content_data.get('nickname'), + self.user.nickname) + + +class UserUpdateTestCase(TestCase): + def setUp(self): + self.factory = RequestFactory() + self.user, self.user_token = create_user_and_token() + + def request(self, user, data, token): + authorization_header = { + 'HTTP_AUTHORIZATION': 'Token {}'.format(token.key), + } + request = self.factory.patch( + '/v2/account/user-profile/{}/'.format(self.user.nickname), + self.factory._encode_data(data, MULTIPART_CONTENT), + MULTIPART_CONTENT, + **authorization_header) + response = UserProfileViewSet.as_view( + {'patch': 'update'})( + request, + pk=user.pk, + nickname=user.nickname, + partial=True + ) + response.render() + content_data = json.loads(response.content) + return (response, content_data,) + + def test_okay(self): + new_locale = 'MaceiĆ³ - Alagoas' + response, content_data = self.request( + self.user, + { + 'locale': new_locale, + }, + self.user_token) + self.assertEqual( + response.status_code, + status.HTTP_200_OK) + self.assertEqual( + content_data.get('locale'), + new_locale) From 25b26988fb31f76736599bbb6efdabbe1b152890 Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Sat, 20 Jul 2019 18:49:22 -0300 Subject: [PATCH 12/15] [fix] Test List User Profile --- bothub/api/v2/tests/test_account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bothub/api/v2/tests/test_account.py b/bothub/api/v2/tests/test_account.py index bbb12021..b28da267 100644 --- a/bothub/api/v2/tests/test_account.py +++ b/bothub/api/v2/tests/test_account.py @@ -291,7 +291,7 @@ def request(self, token): '/v2/account/user-profile/{}/'.format(self.user.nickname) ) response = UserProfileViewSet.as_view( - {'get': 'retrieve'})(request) + {'get': 'retrieve'})(request, nickname=self.user.nickname) response.render() content_data = json.loads(response.content) return (response, content_data,) From 0ba5e681d94deace9f2db5abb6d24921e817d497 Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Tue, 23 Jul 2019 08:54:53 -0300 Subject: [PATCH 13/15] Added test list user profile not exists --- Pipfile.lock | 6 +++--- bothub/api/v2/tests/test_account.py | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 616240ea..a6675b6f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -408,10 +408,10 @@ }, "pyparsing": { "hashes": [ - "sha256:1873c03321fc118f4e9746baf201ff990ceb915f433f23b395f5580d1840cb2a", - "sha256:9b6323ef4ab914af344ba97510e966d64ba91055d6b9afa6b30799340e89cc03" + "sha256:530d8bf8cc93a34019d08142593cf4d78a05c890da8cf87ffa3120af53772238", + "sha256:f78e99616b6f1a4745c0580e170251ef1bbafc0d0513e270c4bd281bf29d2800" ], - "version": "==2.4.0" + "version": "==2.4.1" }, "six": { "hashes": [ diff --git a/bothub/api/v2/tests/test_account.py b/bothub/api/v2/tests/test_account.py index b28da267..79f5077d 100644 --- a/bothub/api/v2/tests/test_account.py +++ b/bothub/api/v2/tests/test_account.py @@ -286,18 +286,21 @@ def setUp(self): self.factory = RequestFactory() self.user, self.user_token = create_user_and_token() - def request(self, token): + def request(self, token, nickname): request = self.factory.get( - '/v2/account/user-profile/{}/'.format(self.user.nickname) + '/v2/account/user-profile/{}/'.format(nickname) ) response = UserProfileViewSet.as_view( - {'get': 'retrieve'})(request, nickname=self.user.nickname) + {'get': 'retrieve'})(request, nickname=nickname) response.render() content_data = json.loads(response.content) return (response, content_data,) def test_okay(self): - response, content_data = self.request(self.user_token) + response, content_data = self.request( + self.user_token, + self.user.nickname + ) self.assertEqual( response.status_code, status.HTTP_200_OK) @@ -305,6 +308,15 @@ def test_okay(self): content_data.get('nickname'), self.user.nickname) + def test_not_exists(self): + response, content_data = self.request(self.user_token, 'no_exists') + self.assertEqual( + response.status_code, + status.HTTP_404_NOT_FOUND) + self.assertEqual( + content_data.get('detail'), + 'Not found.') + class UserUpdateTestCase(TestCase): def setUp(self): From 4cb735cc12906e3ec096e3493559a9cae7d5a475 Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Tue, 23 Jul 2019 10:05:45 -0300 Subject: [PATCH 14/15] Update status_code Response --- bothub/api/v2/account/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bothub/api/v2/account/views.py b/bothub/api/v2/account/views.py index 3cec556a..c79c3b18 100644 --- a/bothub/api/v2/account/views.py +++ b/bothub/api/v2/account/views.py @@ -89,7 +89,7 @@ def update(self, request, *args, **kwargs): if serializer.is_valid(): self.object.set_password(serializer.data.get('password')) self.object.save() - return Response({}, status=status.HTTP_200_OK) + return Response(status=status.HTTP_200_OK) return Response( serializer.errors, @@ -114,7 +114,7 @@ def create(self, request, *args, **kwargs): if serializer.is_valid(): self.object = self.get_object() self.object.send_reset_password_email() - return Response({}) + return Response(status=status.HTTP_201_CREATED) return Response( serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -174,7 +174,7 @@ def update(self, request, *args, **kwargs): if serializer.is_valid(): self.object.set_password(serializer.data.get('password')) self.object.save() - return Response({}) + return Response(status=status.HTTP_200_OK) return Response( serializer.errors, status=status.HTTP_400_BAD_REQUEST) From b694a09a478919b4d305fb9c3d48a40dbb58fcd0 Mon Sep 17 00:00:00 2001 From: Daniel Yohan Date: Tue, 23 Jul 2019 10:15:12 -0300 Subject: [PATCH 15/15] [fix] Tests Account --- bothub/api/v2/tests/test_account.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bothub/api/v2/tests/test_account.py b/bothub/api/v2/tests/test_account.py index 79f5077d..aabfa3fe 100644 --- a/bothub/api/v2/tests/test_account.py +++ b/bothub/api/v2/tests/test_account.py @@ -163,7 +163,7 @@ def request(self, data): response = RequestResetPasswordViewSet.as_view( {'post': 'create'})(request) response.render() - content_data = json.loads(response.content) + content_data = json.loads(response.content or 'null') return (response, content_data,) def test_okay(self): @@ -200,7 +200,7 @@ def request(self, nickname, data): response = ResetPasswordViewSet.as_view( {'post': 'update'})(request, nickname=nickname) response.render() - content_data = json.loads(response.content) + content_data = json.loads(response.content or 'null') return (response, content_data,) def test_okay(self): @@ -251,7 +251,7 @@ def request(self, data, token): response = ChangePasswordViewSet.as_view( {'post': 'update'})(request) response.render() - content_data = json.loads(response.content) + content_data = json.loads(response.content or 'null') return (response, content_data,) def test_okay(self):