From 1324815d6f8093c06f56a5e9aceddf584e833db1 Mon Sep 17 00:00:00 2001 From: dlenskyi Date: Tue, 13 Oct 2020 17:13:52 +0300 Subject: [PATCH 1/7] fully refactored token creation, refreshion, revocation methods, made extra permission for admin api, refactored conditions with user type, fixed tooltip on frontend, fixed translation, fixed custom authorization backend, added extra configs to oauth2 provider, removed redundant requests on owm tokens --- frontend/src-admin/views/CustomersView.vue | 2 +- frontend/src-base/locale/da/da.json | 2 +- frontend/src-base/store/index.js | 10 +- loppeonline/api/v1/admin/api.py | 47 ++- loppeonline/apps/markets/tasks.py | 7 +- loppeonline/apps/users/models.py | 12 +- loppeonline/apps/users/permissions.py | 38 +- loppeonline/apps/users/signals.py | 8 +- loppeonline/apps/users/urls.py | 12 +- loppeonline/apps/users/utils.py | 89 ++--- loppeonline/apps/users/views.py | 417 ++++++++++----------- loppeonline/settings/base.py | 15 +- loppeonline/workers/tasks.py | 14 +- 13 files changed, 332 insertions(+), 341 deletions(-) diff --git a/frontend/src-admin/views/CustomersView.vue b/frontend/src-admin/views/CustomersView.vue index 858125cc..a2377b8e 100644 --- a/frontend/src-admin/views/CustomersView.vue +++ b/frontend/src-admin/views/CustomersView.vue @@ -42,7 +42,7 @@
{ const setCsrfToken = (config) => { const csrfToken = Cookies.get('csrftoken') - if (csrfToken) - config.headers.common['X-CSRFTOKEN'] = csrfToken + if (csrfToken) { + if (config.headers) + config.headers.common['X-CSRFTOKEN'] = csrfToken + else if (config.defaults) + config.defaults.headers.common['X-CSRFTOKEN'] = csrfToken + } } export const adminRequestInterceptor = (config) => { @@ -174,6 +178,7 @@ export const baseActions = { // eslint-disable-next-line no-unused-vars [actionTypes.CHANGE_PASSWORD] ({ commit }, { payloads, axiosInstance }) { + setCsrfToken(axiosInstance) return new Promise((resolve, reject) => { axiosInstance({ data: payloads @@ -189,6 +194,7 @@ export const baseActions = { // eslint-disable-next-line no-unused-vars [actionTypes.CHANGE_LANGUAGE]({ commit }, language) { + setCsrfToken(changeLanguage) // Encode lang data const encodedData = encodedObjectUrl(language) return new Promise((resolve, reject) => { diff --git a/loppeonline/api/v1/admin/api.py b/loppeonline/api/v1/admin/api.py index faa10d2a..48d66c38 100644 --- a/loppeonline/api/v1/admin/api.py +++ b/loppeonline/api/v1/admin/api.py @@ -40,6 +40,7 @@ IsAuthenticated, IsMarketSalesAccess, IsMarketAccess, + IsMarketAdmin, ) from loppeonline.apps.users.tasks import bulk_create_economic_customers from loppeonline.utils.api_filters import ( @@ -68,7 +69,7 @@ class AdminMarketViewSet(viewsets.ModelViewSet): Admin POS view for market operations """ - permission_classes = [IsAuthenticated, IsMarketAccess] + permission_classes = [IsAuthenticated, IsMarketAccess, IsMarketAdmin] serializer_class = AdminMarketModelSerializer lookup_field = "market_id" @@ -89,7 +90,7 @@ class AdminMarketImagesViewSet( """ parser_classes = (MultiPartParser, FormParser, JSONParser) - permission_classes = [IsAuthenticated, IsMarketAccess] + permission_classes = [IsAuthenticated, IsMarketAccess, IsMarketAdmin] serializer_class = MarketImageModelSerializer def get_queryset(self): @@ -104,7 +105,7 @@ class AdminCustomerViewSet(viewsets.ModelViewSet): Admin POS view for customer operations """ - permission_classes = [IsAuthenticated, IsMarketAccess] + permission_classes = [IsAuthenticated, IsMarketAccess, IsMarketAdmin] pagination_class = ViewSetPagination filterset_class = AdminCustomerFilter filter_backends = ( @@ -177,7 +178,7 @@ class AdminMarketShelfViewSet(viewsets.ModelViewSet): Admin view for market shelf operations """ - permission_classes = [IsAuthenticated, IsMarketAccess] + permission_classes = [IsAuthenticated, IsMarketAccess, IsMarketAdmin] filter_backends = (SearchFilter,) search_fields = [ 'name', @@ -213,14 +214,14 @@ def get_queryset(self): date_start = self.request.query_params.get('date_start') date_end = self.request.query_params.get('date_end') if date_start and date_end: - qs = MarketShelf.objects.select_related( - 'market' - ).filter( - market=self.request.user.market_inst - ).exclude( + qs = ( + MarketShelf.objects.select_related('market') + .filter(market=self.request.user.market_inst) + .exclude( bookings__date_start__lte=date_end, bookings__date_end__gte=date_start, ) + ) elif date_start or date_end: raise ValidationError( _('Date start and date end should be together.') @@ -237,7 +238,7 @@ class AdminShelfBookingViewSet(viewsets.ModelViewSet): Admin view for shelf booking operations """ - permission_classes = [IsAuthenticated, IsMarketAccess] + permission_classes = [IsAuthenticated, IsMarketAccess, IsMarketAdmin] filterset_class = AdminShelfBookingsFilter serializer_class = AdminShelfBookingModelSerializer @@ -256,7 +257,7 @@ class AdminCustomerBookingsViewSet(generics.ListAPIView): """ pagination_class = ViewSetPagination - permission_classes = [IsAuthenticated, IsMarketAccess] + permission_classes = [IsAuthenticated, IsMarketAccess, IsMarketAdmin] serializer_class = AdminShelfBookingModelSerializer def get_queryset(self): @@ -276,7 +277,7 @@ class AdminGetCustomerViewSet( Admin POS view for customer operations """ - permission_classes = [IsAuthenticated, IsMarketAccess] + permission_classes = [IsAuthenticated, IsMarketAccess, IsMarketAdmin] serializer_class = AdminGetCustomerModelSerializer lookup_field = 'user_number' @@ -292,7 +293,7 @@ class AdminCustomersSummaryView(generics.ListAPIView): Admin view for getting all customers """ - permission_classes = [IsAuthenticated, IsMarketAccess] + permission_classes = [IsAuthenticated, IsMarketAccess, IsMarketAdmin] serializer_class = AdminCustomersSummarySerializer def get_queryset(self): @@ -305,7 +306,7 @@ def get_queryset(self): class AdminSendEmailConfirmationApiView(generics.GenericAPIView): - permission_classes = [IsAuthenticated, IsMarketAccess] + permission_classes = [IsAuthenticated, IsMarketAccess, IsMarketAdmin] serializer_class = AdminSendEmailConfirmationSerializer def post(self, request, *args, **kwargs): @@ -326,7 +327,12 @@ class AdminResetCustomerPasswordViewSet( View for updating customer password """ - permission_classes = [IsAuthenticated, IsMarketSalesAccess, IsMarketAccess] + permission_classes = [ + IsAuthenticated, + IsMarketSalesAccess, + IsMarketAccess, + IsMarketAdmin, + ] lookup_field = 'user_number' def get_queryset(self): @@ -357,7 +363,12 @@ class AdminVerifyCustomerViewSet( View for updating customer password """ - permission_classes = [IsAuthenticated, IsMarketSalesAccess, IsMarketAccess] + permission_classes = [ + IsAuthenticated, + IsMarketSalesAccess, + IsMarketAccess, + IsMarketAdmin, + ] lookup_field = 'user_number' def get_queryset(self): @@ -385,7 +396,7 @@ def update(self, request, *args, **kwargs): class AdminGetPaymentsInfoView(APIView): - permission_classes = [IsAuthenticated, IsMarketAccess] + permission_classes = [IsAuthenticated, IsMarketAccess, IsMarketAdmin] def get(self, request, export=None, format=None): serializer = AdminGetPaymentsInfoViewSerializer( @@ -411,7 +422,7 @@ class AdminPaymentsInfoSalesPeriodViewSet( mixins.DestroyModelMixin, viewsets.GenericViewSet, ): - permission_classes = [IsAuthenticated, IsMarketAccess] + permission_classes = [IsAuthenticated, IsMarketAccess, IsMarketAdmin] serializer_class = AdminPaymentsInfoSalesPeriodModelSerializer def create(self, request, *args, **kwargs): diff --git a/loppeonline/apps/markets/tasks.py b/loppeonline/apps/markets/tasks.py index 6587945f..4f942f26 100644 --- a/loppeonline/apps/markets/tasks.py +++ b/loppeonline/apps/markets/tasks.py @@ -1,4 +1,5 @@ import datetime +import logging from celery import shared_task @@ -9,6 +10,8 @@ create_sale_lines, ) +logger = logging.getLogger(__name__) + @shared_task def collect_initial_sale_data(market_id): @@ -42,6 +45,6 @@ def collect_initial_sale_data(market_id): data = get_sale_data(token, data['NextPageUrl']) create_sale_lines(data, market_inst) else: - print("Something went wrong while collecting sale data") + logger.warning("Something went wrong while collecting sale data") - print(f"Sale data for market {market_id} successfully collected!") + logger.info(f"Sale data for market {market_id} successfully collected!") diff --git a/loppeonline/apps/users/models.py b/loppeonline/apps/users/models.py index f5dfc382..db6f2d29 100644 --- a/loppeonline/apps/users/models.py +++ b/loppeonline/apps/users/models.py @@ -60,12 +60,16 @@ class Meta: def is_admin(self): return self.user_type == self.ADMIN + @property + def is_customer(self): + return self.user_type == self.CUSTOMER + @property def market_inst(self): - if self.user_type == CustomUser.ADMIN and hasattr(self, 'market'): + if self.is_admin and hasattr(self, 'market'): return self.market if ( - self.user_type == CustomUser.CUSTOMER + self.is_customer and hasattr(self, 'customer') and self.customer.market ): @@ -74,13 +78,13 @@ def market_inst(self): def __str__(self): if self.get_username(): return self.get_username() - if self.user_type == CustomUser.CUSTOMER: + if self.is_customer: if hasattr(self, 'customer') and self.customer.market: return gettext('Customer {} for {}').format( self.customer.user_number, self.customer.market.market_id, ) return gettext('Customer') - if self.user_type == CustomUser.ADMIN: + if self.is_admin: return gettext('Admin for market {}').format(self.market.market_id) diff --git a/loppeonline/apps/users/permissions.py b/loppeonline/apps/users/permissions.py index d0ce4033..04a1c19a 100644 --- a/loppeonline/apps/users/permissions.py +++ b/loppeonline/apps/users/permissions.py @@ -3,35 +3,6 @@ from django.core.exceptions import ObjectDoesNotExist -class IsCustomerEmailOwner(BasePermission): - def has_permission(self, request, view): - try: - user = CustomUser.objects.get(email=view.kwargs['email']) - if request.user == user: - return True - else: - return False - except ObjectDoesNotExist: - return False - - def has_object_permission(self, request, view, obj): - return obj == request.user - - -class IsCustomerUserNumberOwner(BasePermission): - def has_permission(self, request, view): - try: - customer = Customer.objects.get( - user_number=view.kwargs['user_number'] - ) - if request.user == customer.user: - return True - else: - return False - except ObjectDoesNotExist: - return False - - class IsAdminOwner(BasePermission): def has_permission(self, request, view): try: @@ -56,6 +27,15 @@ def has_permission(self, request, view): return request.user.market_inst is not None +class IsMarketAdmin(BasePermission): + """ + Check if user instance has admin user type of particular market + """ + + def has_permission(self, request, view): + return request.user.market_inst is not None and request.user.is_admin + + class IsMarketSalesAccess(BasePermission): def has_permission(self, request, view): return request.user.market_inst.sales_access diff --git a/loppeonline/apps/users/signals.py b/loppeonline/apps/users/signals.py index 3c2cd7fc..d050885b 100644 --- a/loppeonline/apps/users/signals.py +++ b/loppeonline/apps/users/signals.py @@ -12,7 +12,7 @@ @receiver(post_save, sender=CustomUser) def create_user_market(sender, instance, created, **kwargs): if created: - if instance.user_type == CustomUser.ADMIN: + if instance.is_admin: Market.objects.create(user=instance) @@ -21,14 +21,14 @@ def save_user_market(sender, instance, **kwargs): try: instance.market.save() except ObjectDoesNotExist: - if instance.user_type == CustomUser.ADMIN: + if instance.is_admin: Market.objects.create(user=instance) @receiver(post_save, sender=CustomUser) def create_user_customer(sender, instance, created, **kwargs): if created: - if instance.user_type == CustomUser.CUSTOMER: + if instance.is_customer: Customer.objects.create(user=instance) @@ -37,7 +37,7 @@ def save_user_customer(sender, instance, **kwargs): try: instance.customer.save() except ObjectDoesNotExist: - if instance.user_type == CustomUser.CUSTOMER: + if instance.is_customer: Customer.objects.create(user=instance) diff --git a/loppeonline/apps/users/urls.py b/loppeonline/apps/users/urls.py index 0f88be93..16e5592d 100644 --- a/loppeonline/apps/users/urls.py +++ b/loppeonline/apps/users/urls.py @@ -9,9 +9,9 @@ SendFeedbackAPIView, get_reset_password_data, get_email_confirmation_data, - token, - refresh_token, - token_revocation, + MyTokenView, + MyRefreshTokenView, + MyRevokeTokenView, ) router = routers.DefaultRouter() @@ -48,7 +48,7 @@ get_email_confirmation_data, name='get-email-confirmation-data', ), - path('token/', token, name="login"), - path('token/refresh/', refresh_token, name="re-login"), - path('token/revoke/', token_revocation, name="logout"), + path('token/', MyTokenView.as_view(), name="login"), + path('token/refresh/', MyRefreshTokenView.as_view(), name="re-login"), + path('token/revoke/', MyRevokeTokenView.as_view(), name="logout"), ] diff --git a/loppeonline/apps/users/utils.py b/loppeonline/apps/users/utils.py index 4b6d1df3..8f8a1d02 100644 --- a/loppeonline/apps/users/utils.py +++ b/loppeonline/apps/users/utils.py @@ -1,49 +1,50 @@ +from django.contrib.auth import authenticate from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.core.mail import EmailMultiAlternatives +from django.http import JsonResponse from django.template.loader import render_to_string from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode -from oauth2_provider.models import AccessToken, Application, RefreshToken -from datetime import timedelta from django.conf import settings -from oauthlib import common -from django.utils import timezone +from django.utils.translation import gettext as _ +from oauth2_provider.oauth2_validators import OAuth2Validator +from rest_framework.status import HTTP_403_FORBIDDEN from loppeonline.apps.logger.logger_manager import LoggerManager -from loppeonline.apps.users.models import CustomUser, Customer - +from loppeonline.apps.markets.models import Market +from loppeonline.apps.users.models import Customer lm_auth = LoggerManager(type_key='USER', case_key='AUTH') -class Oauth2Utils: - def __init__(self, user): - self.refresh_token = '' - self.access_token = '' - self.user = user - self.application = Application.objects.get( - name=settings.OAUTH_APPLICATION_NAME - ) - - def get_tokens(self): - expires = timezone.now() + timedelta( - seconds=settings.ACCESS_TOKEN_EXPIRE_SECONDS - ) - self.access_token = AccessToken( - user=self.user, - scope='', - expires=expires, - token=common.generate_token(), - application=self.application, - ) - self.access_token.save() - self.refresh_token = RefreshToken( - user=self.user, - token=common.generate_token(), - application=self.application, - access_token=self.access_token, - ) - self.refresh_token.save() +class MyOAuth2Validator(OAuth2Validator): + def validate_user( + self, username, password, client, request, *args, **kwargs + ): + """ + Check username and password correspond to a valid and active User + """ + market_id = dict(request.decoded_body).get('market_id') + if market_id: + try: + market = Market.objects.get(market_id__iexact=market_id) + except Market.DoesNotExist: + return JsonResponse( + {'error': _('Such market does not exist')}, + status=HTTP_403_FORBIDDEN, + ) + u = authenticate( + username=username, + password=password, + market=market, + customer=True, + ) + else: + u = authenticate(username=username, password=password) + if u is not None and u.is_active: + request.user = u + return True + return False def send_email_confirmation(user): @@ -57,7 +58,7 @@ def send_email_confirmation(user): mail_subject = ''.join(mail_subject.splitlines()) template_name = ( 'account/email/customer_email_confirmation_message.html' - if user.user_type == CustomUser.CUSTOMER + if user.is_customer else 'account/email/admin_email_confirmation_message.html' ) @@ -65,23 +66,23 @@ def send_email_confirmation(user): template_name, { 'market_id': customer.market.market_id - if user.user_type == CustomUser.CUSTOMER + if user.is_customer else None, 'market_name': customer.market.visual_name - if user.user_type == CustomUser.CUSTOMER + if user.is_customer else None, 'market_address': customer.market.support_address - if user.user_type == CustomUser.CUSTOMER + if user.is_customer else None, 'market_phone': customer.market.support_phone - if user.user_type == CustomUser.CUSTOMER + if user.is_customer else None, 'market_email': customer.market.support_email - if user.user_type == CustomUser.CUSTOMER + if user.is_customer else None, 'user': user, 'user_first_name': customer.user.first_name - if user.user_type == CustomUser.CUSTOMER + if user.is_customer else None, 'user_number': customer.user_number if customer else None, 'email': user.email, @@ -111,7 +112,7 @@ def send_email_reset_confirmation(user): mail_subject = ''.join(mail_subject.splitlines()) template_name = ( 'account/email/customer_email_reset_confirmation_message.html' - if user.user_type == CustomUser.CUSTOMER + if user.is_customer else 'account/email/admin_email_reset_confirmation_message.html' ) @@ -119,10 +120,10 @@ def send_email_reset_confirmation(user): template_name, { 'market_id': customer.market.market_id - if user.user_type == CustomUser.CUSTOMER + if user.is_customer else None, 'market_name': customer.market.visual_name - if user.user_type == CustomUser.CUSTOMER + if user.is_customer else None, 'user': user, 'email': user.email, diff --git a/loppeonline/apps/users/views.py b/loppeonline/apps/users/views.py index 28ee2915..c21768bf 100644 --- a/loppeonline/apps/users/views.py +++ b/loppeonline/apps/users/views.py @@ -1,15 +1,31 @@ +import json + import requests +from django.conf import settings +from django.contrib.auth import get_user_model, authenticate from django.contrib.auth.backends import ModelBackend +from django.contrib.auth.tokens import default_token_generator from django.core.validators import ValidationError +from django.http import JsonResponse from django.utils.decorators import method_decorator +from django.utils.encoding import force_text from django.utils.translation import gettext as _ +from django.utils.http import urlsafe_base64_decode as uid_decoder from django.views.decorators.debug import sensitive_post_parameters -from oauth2_provider.models import RefreshToken -from oauth2_provider.oauth2_validators import OAuth2Validator +from oauth2_provider.models import ( + RefreshToken, + Application, + get_access_token_model, +) +from oauth2_provider.signals import app_authorized +from oauth2_provider.views import TokenView, RevokeTokenView from rest_auth.serializers import PasswordChangeSerializer from rest_framework import generics, viewsets, mixins from rest_framework import status -from rest_framework.decorators import api_view, permission_classes +from rest_framework.decorators import ( + api_view, + permission_classes, +) from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.status import ( @@ -21,13 +37,32 @@ from loppeonline.apps.logger.logger_manager import LoggerManager from loppeonline.apps.markets.models import Market from loppeonline.utils.mixins import ViewSetPagination -from .permissions import * -from .serializers import * +from .models import CustomUser, Customer +from .permissions import ( + IsAdminOwner, + IsMarketAccess, + IsMarketSalesAccess, + IsMarketApiValid, + IsAuthenticated, +) +from .serializers import ( + SendFeedbackSerializer, + CustomerSerializer, + UserSerializer, + CustomUserSerializer, + PasswordResetSerializer, + AdminEmailConfirmSerializer, + CustomPasswordResetEmailConfirmSerializer, + RegisterUserModelSerializer, +) from .utils import ( - Oauth2Utils, send_admin_feedback_email, send_customer_feedback_email, + send_email_reset_confirmation, + MyOAuth2Validator, ) +from loppeonline.utils.helpers import caseless_equal + lm = LoggerManager(type_key='USER', case_key='AUTH') lm_settings = LoggerManager(type_key='USER', case_key='AUTH_SETTING') @@ -35,12 +70,10 @@ UserModel = get_user_model() -# Class to permit the authentication using email or username -# Requires to define two functions: -# - authenticate -# - get_user class CustomBackend(ModelBackend): - def authenticate(self, username=None, password=None, **kwargs): + def authenticate( + self, request=None, username=None, password=None, **kwargs + ): is_customer_login = kwargs.get('customer') market = kwargs.get('market') @@ -325,8 +358,10 @@ def perform_update(self, serializer): access_token = self.request.META.get('HTTP_AUTHORIZATION') if access_token: access_token = access_token.split(' ')[1] - Oauth2Utils(user=self.request.user).remove_oauth_token( - access_token, self.request + MyOAuth2Validator().revoke_token( + token=access_token, + token_type_hint='access_token', + request=self.request, ) user.save() @@ -386,7 +421,7 @@ def get_serializer_class(self): else: user = get_user(uid) if user: - if user.user_type == CustomUser.ADMIN: + if user.is_admin: return AdminEmailConfirmSerializer else: return CustomPasswordResetEmailConfirmSerializer @@ -520,245 +555,179 @@ def get_email_confirmation_data(request): ) -@api_view(['POST']) -@permission_classes([AllowAny]) -def token(request): - """ - Gets tokens with username and password. - Input should be in the format: - { - "username": "token", - "password": "A1qwerty" - } - """ - - required_params = ['username', 'password'] - - keys = request.data - - if not all([r_param in keys for r_param in required_params]): - return Response( - {'error': _('Please provide both username and password')}, - status=HTTP_400_BAD_REQUEST, - ) +class MyTokenView(TokenView): + @method_decorator(sensitive_post_parameters("password")) + def post(self, request, *args, **kwargs): + """ + Gets tokens with username and password. + Input should be in the format: + { + "username": "token", + "password": "A1qwerty" + } + """ + body_unicode = request.body.decode('utf-8') + if not body_unicode: + return super().post(request, *args, **kwargs) + data = json.loads(body_unicode) - username = request.data.get('username') - password = request.data.get('password') - market_id = request.data.get('market_id') + required_params = ['username', 'password'] - if username is None or password is None: - return Response( - {'error': _('Please provide both username and password')}, - status=HTTP_400_BAD_REQUEST, - ) - - # If customer tries to login, then we should - # check by email, otherwise by username - if market_id: - try: - market = Market.objects.get(market_id__iexact=market_id) - except Market.DoesNotExist: - return Response( - {'error': _('Such market does not exist')}, - status=HTTP_403_FORBIDDEN, + if not all([r_param in data for r_param in required_params]): + return JsonResponse( + {'error': _('Please provide both username and password')}, + status=HTTP_400_BAD_REQUEST, ) - user = CustomBackend().authenticate( - username=username, password=password, market=market, customer=True - ) - else: - user = CustomBackend().authenticate( - username=username, password=password - ) - if not user: - return Response( - {'error': _('Invalid Credentials')}, status=HTTP_400_BAD_REQUEST - ) - if not user.verified: - return Response( - {'error': _('Email not verified')}, status=HTTP_403_FORBIDDEN - ) - if not user.is_active: - return Response( - {'error': _('Account was blocked')}, status=HTTP_403_FORBIDDEN - ) + username = data.get('username') + password = data.get('password') + market_id = data.get('market_id') - # Check recaptcha only for admins - if user.user_type == CustomUser.ADMIN: - res, reason = check_recaptcha(request) - - if not res: - return Response( - {'error': _('Recaptcha error: {}'.format(reason))}, - status=HTTP_403_FORBIDDEN, + if username is None or password is None: + return JsonResponse( + {'error': _('Please provide both username and password')}, + status=HTTP_400_BAD_REQUEST, ) - user_market_id = None - if market_id: - try: - customer = Customer.objects.get(user=user) - except Customer.DoesNotExist: - customer = None + # If customer tries to login, then we should + # check by email, otherwise by username + if market_id: + try: + market = Market.objects.get(market_id__iexact=market_id) + except Market.DoesNotExist: + return JsonResponse( + {'error': _('Such market does not exist')}, + status=HTTP_403_FORBIDDEN, + ) + user = authenticate( + username=username, + password=password, + market=market, + customer=True, + ) + else: + user = authenticate(username=username, password=password) - if ( - customer - and customer.market - and market_id != customer.market.market_id - ): - return Response( - {'error': _('Enter Market ID related to this user')}, - status=HTTP_403_FORBIDDEN, + if not user: + return JsonResponse( + {'error': _('Invalid Credentials')}, + status=HTTP_400_BAD_REQUEST, ) - if not customer: - return Response( - {'error': _('Such customer does not exist')}, - status=HTTP_403_FORBIDDEN, + if not user.verified: + return JsonResponse( + {'error': _('Email not verified')}, status=HTTP_403_FORBIDDEN + ) + if not user.is_active: + return JsonResponse( + {'error': _('Account was blocked')}, status=HTTP_403_FORBIDDEN ) - # Trying to get market id, that connected to current user, - # if it's not a Market Admin, then try to search by customer - # If no market for customer - then error - user_market_id = customer.market.market_id + # Check recaptcha only for admins + if user.is_admin: + res, reason = check_recaptcha(data) - if not user_market_id: - try: - user_market_id = user.market.market_id - except ObjectDoesNotExist: - return Response( - {'error': _('Such market does not belong to this user')}, + if not res: + return JsonResponse( + {'error': _('Recaptcha error: {}'.format(reason))}, + status=HTTP_403_FORBIDDEN, + ) + + if not user.market_inst.sales_access and user.is_customer: + return JsonResponse( + {'error': _('You do not have permission to do this action.')}, status=HTTP_403_FORBIDDEN, ) - try: - market = Market.objects.get(market_id__iexact=user_market_id) - except Market.DoesNotExist: - return Response( - {'error': _('Such market does not exist')}, - status=HTTP_403_FORBIDDEN, + # Generate token + application = Application.objects.get( + name=settings.OAUTH_APPLICATION_NAME ) + data['grant_type'] = 'password' + data['client_id'] = application.client_id + data['client_secret'] = application.client_secret + request._body = json.dumps(data).encode('utf-8') + url, headers, body, status = self.create_token_response(request) + body = json.loads(body) + if status == 200: + access_token = body.get("access_token") + if access_token is not None: + token = get_access_token_model().objects.get( + token=access_token + ) + app_authorized.send(sender=self, request=request, token=token) + # SET ADDITIONAL DATA + body['username'] = user.username + body['email'] = user.email + body['market_id'] = user.market_inst.market_id + body['flexpos_api_valid'] = user.market_inst.flexpos_api_valid + body['sales_access'] = user.market_inst.sales_access + body['is_admin'] = user.is_admin - if not market.sales_access and user.user_type == CustomUser.CUSTOMER: - return Response( - {'error': _('You do not have permission to do this action.')}, - status=HTTP_403_FORBIDDEN, - ) + # LOGGING SIGN_IN + lm.set_db_log(meta=request.META, user=user.username, log_key='SIGN_IN') - oauth = Oauth2Utils(user=user) - oauth.get_tokens() + response = JsonResponse(body, status=status, safe=False) - # LOGGING SIGN_IN - lm.set_db_log(meta=request.META, user=user.username, log_key='SIGN_IN') + for k, v in headers.items(): + response[k] = v + return response - return Response( - { - 'grant_type': 'password', - 'expires_in': settings.ACCESS_TOKEN_EXPIRE_SECONDS, - 'token_type': 'Bearer', - 'refresh_token': str(oauth.refresh_token), - 'access_token': str(oauth.access_token), - 'username': user.username, - 'email': user.email, - 'market_id': market.market_id, - 'flexpos_api_valid': market.flexpos_api_valid, - 'sales_access': market.sales_access, - 'is_admin': user.is_admin, - } - ) +class MyRefreshTokenView(TokenView): + @method_decorator(sensitive_post_parameters("password")) + def post(self, request, *args, **kwargs): + """ + Refreshes token for user. Input should be in the format: + { + "refresh_token": "" + } + """ -@api_view(['POST']) -@permission_classes([AllowAny]) -def refresh_token(request): - """ - Refreshes token for user. Input should be in the format: - { - "refresh_token": "" - } - """ - keys = request.data + body_unicode = request.body.decode('utf-8') + if not body_unicode: + return super().post(request, *args, **kwargs) + data = json.loads(body_unicode) - if not ('refresh_token' in keys): - return Response( - {'error': _('Please provide refresh_token')}, - status=HTTP_400_BAD_REQUEST, - ) - try: - refresh_token_from_db = RefreshToken.objects.get( - token=request.data['refresh_token'] - ) - except RefreshToken.DoesNotExist: - return Response( - {'error': _('Refresh token not found')}, - status=HTTP_400_BAD_REQUEST, - ) - try: - token_response = requests.post( - settings.HOST + '/o/token/', - data={ - 'grant_type': 'refresh_token', - 'refresh_token': refresh_token_from_db.token, - 'client_id': refresh_token_from_db.application.client_id, - 'client_secret': refresh_token_from_db.application.client_secret, - }, - ) - token_response.raise_for_status() - except requests.HTTPError as e: - # If AccessToken already revoked, - # but RefreshToken remained - revoke it too - if token_response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR: - refresh_token_from_db.revoke() + if not ('refresh_token' in data): return Response( - {'error': _('Cannot refresh. Token already revoked')}, - status=status.HTTP_403_FORBIDDEN, + {'error': _('Please provide refresh_token')}, + status=HTTP_400_BAD_REQUEST, + ) + try: + refresh_token_from_db = RefreshToken.objects.get( + token=data['refresh_token'] + ) + except RefreshToken.DoesNotExist: + return JsonResponse( + {'error': _('Refresh token not found')}, + status=HTTP_400_BAD_REQUEST, ) - return Response(e.response.text, status=status.HTTP_403_FORBIDDEN) - except requests.RequestException as e: - return Response({'error': e}, status=status.HTTP_403_FORBIDDEN) - - token_response = token_response.json() - # Check for errors in response, if it occurs, then return error - error = token_response.get('error') - if error: - return Response({'error': error}, status=status.HTTP_403_FORBIDDEN) + # Update request body + data['grant_type'] = 'refresh_token' + data['client_id'] = refresh_token_from_db.application.client_id + data['client_secret'] = refresh_token_from_db.application.client_secret + request._body = json.dumps(data).encode('utf-8') - # In case token is valid return info about new token - return Response( - { - 'token_type': token_response['token_type'], - 'scope': token_response['scope'], - 'access_token': token_response['access_token'], - 'expires_in': settings.ACCESS_TOKEN_EXPIRE_SECONDS, - 'refresh_token': token_response['refresh_token'], - } - ) + return super().post(request, *args, **kwargs) -@api_view(['POST']) -@permission_classes([AllowAny]) -def token_revocation(request): - """ - Method to revoke tokens. - { - "token": "" - } - """ - keys = request.data +class MyRevokeTokenView(RevokeTokenView): + def post(self, request, *args, **kwargs): + body_unicode = request.body.decode('utf-8') + if not body_unicode: + return super().post(request, *args, **kwargs) + data = json.loads(body_unicode) - if not ('token' in keys): - return Response( - {'error': _('Please provide token')}, status=HTTP_400_BAD_REQUEST + application = Application.objects.get( + name=settings.OAUTH_APPLICATION_NAME ) - OAuth2Validator().revoke_token( - token=request.data['token'], - token_type_hint='access_token', - request=request - ) - - lm_auth = LoggerManager(type_key='USER', case_key='AUTH') - lm_auth.set_db_log( - meta=request.META, user=request.user.username, log_key='LOGOUT' - ) - - return Response({'message': _('Token revoked')}, status=status.HTTP_200_OK) + data['client_id'] = application.client_id + data['client_secret'] = application.client_secret + request._body = json.dumps(data).encode('utf-8') + lm_auth = LoggerManager(type_key='USER', case_key='AUTH') + lm_auth.set_db_log( + meta=request.META, user=request.user.username, log_key='LOGOUT' + ) + return super().post(request, *args, **kwargs) diff --git a/loppeonline/settings/base.py b/loppeonline/settings/base.py index 0164d9f1..c82770b7 100644 --- a/loppeonline/settings/base.py +++ b/loppeonline/settings/base.py @@ -393,10 +393,13 @@ 'write': 'Write scope', 'groups': 'Access to your groups', }, + 'OAUTH2_BACKEND_CLASS': 'oauth2_provider.oauth2_backends.JSONOAuthLibCore', + 'ACCESS_TOKEN_EXPIRE_SECONDS': 7 * 86400, # Token will expire in 7 days + 'REFRESH_TOKEN_EXPIRE_SECONDS': 7 * 86400, # Token will expire in 7 days + 'OAUTH2_VALIDATOR_CLASS': 'loppeonline.apps.users.utils.MyOAuth2Validator', } OAUTH_APPLICATIONS = False # Hide\Show oauth2 applications endpoints -OAUTH_DELETE_EXPIRED = (True,) OLD_PASSWORD_FIELD_ENABLED = True @@ -446,8 +449,6 @@ LOGIN_REDIRECT_URL = '/' -ACCESS_TOKEN_EXPIRE_SECONDS = 7 * 86400 # Token will expire in 7 days - # List of allowed for using recaptcha environment names from list => [DEV, STAGING, PRODUCTION] USE_RECAPTCHA_ENVS = ['PRODUCTION'] USE_RECAPTCHA = SERVER_TYPE in USE_RECAPTCHA_ENVS @@ -475,7 +476,13 @@ 'schedule': datetime.timedelta( minutes=COLLECT_SALES_MINUTE_INTERVAL ), # Collect sale data every *constant* minute - } + }, + 'clear_expired_tokens': { + 'task': 'clear_expired_tokens', + 'schedule': datetime.timedelta( + hours=12 + ), # Clear expired authorization tokens every 12 hours + }, } ROOT_LOG = 'logs' diff --git a/loppeonline/workers/tasks.py b/loppeonline/workers/tasks.py index bc4dd302..cc42eec3 100644 --- a/loppeonline/workers/tasks.py +++ b/loppeonline/workers/tasks.py @@ -1,4 +1,7 @@ import datetime +import logging + +from oauth2_provider.models import clear_expired from celery import shared_task from django.conf import settings @@ -10,6 +13,8 @@ create_sale_lines, ) +logger = logging.getLogger(__name__) + @shared_task(name='collect_sale_data') def collect_sale_data(): @@ -47,6 +52,11 @@ def collect_sale_data(): else: break else: - print("Something went wrong while collecting sale data") + logger.warning("Something went wrong while collecting sale data") + + logger.info("Sale data collected") + - print("Sale data collected") +@shared_task(name='clear_expired_tokens') +def clear_expired_tokens(): + clear_expired() From ce81e8d7485968f2ea0c19f26617b222e659198d Mon Sep 17 00:00:00 2001 From: dlenskyi Date: Thu, 15 Oct 2020 15:09:23 +0300 Subject: [PATCH 2/7] optimized code --- frontend/src-base/store/index.js | 24 ++++++++++++++++-------- loppeonline/api/v1/user/api.py | 4 +++- loppeonline/apps/users/permissions.py | 13 +++++++++++-- loppeonline/apps/users/tasks.py | 1 + loppeonline/apps/users/views.py | 12 ++++++++---- 5 files changed, 39 insertions(+), 15 deletions(-) diff --git a/frontend/src-base/store/index.js b/frontend/src-base/store/index.js index 89e95e2e..02c60d78 100644 --- a/frontend/src-base/store/index.js +++ b/frontend/src-base/store/index.js @@ -59,12 +59,13 @@ export const refreshTokenCheck = (token, failCallback) => { const setCsrfToken = (config) => { const csrfToken = Cookies.get('csrftoken') - if (csrfToken) { - if (config.headers) - config.headers.common['X-CSRFTOKEN'] = csrfToken - else if (config.defaults) - config.defaults.headers.common['X-CSRFTOKEN'] = csrfToken - } + if (csrfToken) + config.headers.common['X-CSRFTOKEN'] = csrfToken +} + +export const baseRequestInterceptor = (config) => { + setCsrfToken(config) + return config } export const adminRequestInterceptor = (config) => { @@ -112,6 +113,15 @@ export const customerResponseRejectedInterceptor = error => { return Promise.reject(error) } +const axiosInstances = [ + refreshToken, + changeLanguage, +] + +for (const instance of axiosInstances) { + instance.interceptors.request.use(baseRequestInterceptor) +} + export const baseState = { showLoader: true, user: { @@ -178,7 +188,6 @@ export const baseActions = { // eslint-disable-next-line no-unused-vars [actionTypes.CHANGE_PASSWORD] ({ commit }, { payloads, axiosInstance }) { - setCsrfToken(axiosInstance) return new Promise((resolve, reject) => { axiosInstance({ data: payloads @@ -194,7 +203,6 @@ export const baseActions = { // eslint-disable-next-line no-unused-vars [actionTypes.CHANGE_LANGUAGE]({ commit }, language) { - setCsrfToken(changeLanguage) // Encode lang data const encodedData = encodedObjectUrl(language) return new Promise((resolve, reject) => { diff --git a/loppeonline/api/v1/user/api.py b/loppeonline/api/v1/user/api.py index eb5e8f29..4597c3ff 100644 --- a/loppeonline/api/v1/user/api.py +++ b/loppeonline/api/v1/user/api.py @@ -1,4 +1,3 @@ -from oauth2_provider.contrib.rest_framework import OAuth2Authentication from rest_framework import viewsets, mixins from loppeonline.apps.markets.models import Market @@ -8,6 +7,7 @@ IsMarketApiValid, IsMarketSalesAccess, IsMarketAccess, + IsMarketCustomer, ) from loppeonline.utils.api_filters import UserSaleLinesFilter @@ -27,6 +27,7 @@ class UserGetMarketViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet): IsMarketApiValid, IsMarketSalesAccess, IsMarketAccess, + IsMarketCustomer, ] serializer_class = UserGetMarketModelSerializer lookup_field = 'market_id' @@ -48,6 +49,7 @@ class UserGetSaleLines(mixins.ListModelMixin, viewsets.GenericViewSet): IsMarketApiValid, IsMarketSalesAccess, IsMarketAccess, + IsMarketCustomer, ] serializer_class = UserGetSaleLinesModelSerializer filterset_class = UserSaleLinesFilter diff --git a/loppeonline/apps/users/permissions.py b/loppeonline/apps/users/permissions.py index 04a1c19a..ec682f63 100644 --- a/loppeonline/apps/users/permissions.py +++ b/loppeonline/apps/users/permissions.py @@ -29,11 +29,20 @@ def has_permission(self, request, view): class IsMarketAdmin(BasePermission): """ - Check if user instance has admin user type of particular market + Check if user instance has admin user type of """ def has_permission(self, request, view): - return request.user.market_inst is not None and request.user.is_admin + return request.user.is_admin + + +class IsMarketCustomer(BasePermission): + """ + Check if user instance has customer user type + """ + + def has_permission(self, request, view): + return request.user.is_customer class IsMarketSalesAccess(BasePermission): diff --git a/loppeonline/apps/users/tasks.py b/loppeonline/apps/users/tasks.py index 7bbb2185..46b09dbb 100644 --- a/loppeonline/apps/users/tasks.py +++ b/loppeonline/apps/users/tasks.py @@ -14,6 +14,7 @@ from loppeonline.apps.markets.models import Market from loppeonline.utils.economic import EconomicManager + logger = logging.getLogger(__name__) diff --git a/loppeonline/apps/users/views.py b/loppeonline/apps/users/views.py index 31c7f209..e01e44ce 100644 --- a/loppeonline/apps/users/views.py +++ b/loppeonline/apps/users/views.py @@ -43,7 +43,7 @@ IsMarketAccess, IsMarketSalesAccess, IsMarketApiValid, - IsAuthenticated, + IsAuthenticated, IsMarketAdmin, IsMarketCustomer, ) from .serializers import ( SendFeedbackSerializer, @@ -368,7 +368,12 @@ def perform_update(self, serializer): class AdminRetrieveView(UserRetrieveView): - permission_classes = [IsAuthenticated, IsAdminOwner, IsMarketAccess] + permission_classes = [ + IsAuthenticated, + IsAdminOwner, + IsMarketAccess, + IsMarketAdmin, + ] class CustomerRetrieveView(UserRetrieveView): @@ -377,6 +382,7 @@ class CustomerRetrieveView(UserRetrieveView): IsMarketApiValid, IsMarketSalesAccess, IsMarketAccess, + IsMarketCustomer, ] serializer_class = CustomerSerializer lookup_field = 'user__email' @@ -589,8 +595,6 @@ def post(self, request, *args, **kwargs): status=HTTP_400_BAD_REQUEST, ) - # If customer tries to login, then we should - # check by email, otherwise by username if market_id: try: market = Market.objects.get(market_id__iexact=market_id) From cfecba6b492a46f33e07af525ea225a27d6ca901 Mon Sep 17 00:00:00 2001 From: dlenskyi Date: Thu, 15 Oct 2020 16:05:04 +0300 Subject: [PATCH 3/7] refactored admin and customer endpoint --- frontend/src-admin/services/index.js | 2 +- frontend/src-admin/store/index.js | 9 ++---- frontend/src-user/services/index.js | 2 +- frontend/src-user/store/index.js | 7 ++-- loppeonline/apps/users/permissions.py | 17 ---------- loppeonline/apps/users/urls.py | 4 +-- loppeonline/apps/users/views.py | 46 ++++++++++++--------------- loppeonline/settings/base.py | 6 ++-- 8 files changed, 34 insertions(+), 59 deletions(-) diff --git a/frontend/src-admin/services/index.js b/frontend/src-admin/services/index.js index 1f9e2b1a..fe387738 100644 --- a/frontend/src-admin/services/index.js +++ b/frontend/src-admin/services/index.js @@ -18,7 +18,7 @@ export const getAdmin = axios.create({ }) export const updateAdmin = axios.create({ - method: 'patch', + method: 'put', baseURL: `${userConfig.baseURL}/admin/`, headers: { 'Accept': 'application/json', diff --git a/frontend/src-admin/store/index.js b/frontend/src-admin/store/index.js index 0bafe981..c0384b95 100644 --- a/frontend/src-admin/store/index.js +++ b/frontend/src-admin/store/index.js @@ -293,10 +293,9 @@ export default new Vuex.Store({ }) }, - [actionTypes.GET_ADMIN_DATA]({ commit, state }) { + [actionTypes.GET_ADMIN_DATA]({ commit }) { return new Promise((resolve, reject) => { - const name = encodeURIComponent(state.user.name) - getAdmin(`/${name}/`) + getAdmin() .then((response) => { commit(mutationTypes.SET_USER_DATA, response.data) resolve(response.data) @@ -307,11 +306,9 @@ export default new Vuex.Store({ }) }, - [actionTypes.CHANGE_ADMIN_DATA] ({commit, state}, payloads) { + [actionTypes.CHANGE_ADMIN_DATA] ({commit}, payloads) { return new Promise((resolve, reject) => { - const name = encodeURIComponent(state.user.name) updateAdmin({ - url: `/${name}/`, data: payloads }) .then((response) => { diff --git a/frontend/src-user/services/index.js b/frontend/src-user/services/index.js index a2d96238..c0d4ef73 100644 --- a/frontend/src-user/services/index.js +++ b/frontend/src-user/services/index.js @@ -18,7 +18,7 @@ export const customerGet = axios.create({ }) export const updateCustomer = axios.create({ - method: 'patch', + method: 'put', baseURL: `${userConfig.baseURL}/customer/`, headers: { 'Accept': 'application/json', diff --git a/frontend/src-user/store/index.js b/frontend/src-user/store/index.js index bf128e98..8f1b9e26 100644 --- a/frontend/src-user/store/index.js +++ b/frontend/src-user/store/index.js @@ -85,9 +85,9 @@ export default new Vuex.Store({ actions: { ...baseActions, - [actionTypes.GET_CUSTOMER_DATA]({ commit, state }) { + [actionTypes.GET_CUSTOMER_DATA]({ commit }) { return new Promise((resolve, reject) => { - customerGet(`/${state.user.email}/`) + customerGet() .then((response) => { commit(mutationTypes.SET_USER_DATA, response.data) commit(mutationTypes.SET_CUSTOMER_DATA, response.data) @@ -99,10 +99,9 @@ export default new Vuex.Store({ }) }, - [actionTypes.CHANGE_CUSTOMER_DATA] ({commit, state}, payloads) { + [actionTypes.CHANGE_CUSTOMER_DATA] ({commit}, payloads) { return new Promise((resolve, reject) => { updateCustomer({ - url: `/${state.user.email}/`, data: payloads }) .then((response) => { diff --git a/loppeonline/apps/users/permissions.py b/loppeonline/apps/users/permissions.py index ec682f63..54ff5c09 100644 --- a/loppeonline/apps/users/permissions.py +++ b/loppeonline/apps/users/permissions.py @@ -1,21 +1,4 @@ from rest_framework.permissions import BasePermission -from .models import CustomUser, Customer -from django.core.exceptions import ObjectDoesNotExist - - -class IsAdminOwner(BasePermission): - def has_permission(self, request, view): - try: - user = CustomUser.objects.get(username=view.kwargs['username']) - if request.user == user: - return True - else: - return False - except ObjectDoesNotExist: - return False - - def has_object_permission(self, request, view, obj): - return obj == request.user class IsMarketAccess(BasePermission): diff --git a/loppeonline/apps/users/urls.py b/loppeonline/apps/users/urls.py index 16e5592d..c5325172 100644 --- a/loppeonline/apps/users/urls.py +++ b/loppeonline/apps/users/urls.py @@ -25,12 +25,12 @@ name='register-user', ), path( - 'admin//', + 'admin/', AdminRetrieveView.as_view(), name='retrieve-admin', ), path( - 'customer//', + 'customer/', CustomerRetrieveView.as_view(), name='retrieve-customer', ), diff --git a/loppeonline/apps/users/views.py b/loppeonline/apps/users/views.py index e01e44ce..ba776bcd 100644 --- a/loppeonline/apps/users/views.py +++ b/loppeonline/apps/users/views.py @@ -20,7 +20,7 @@ from oauth2_provider.signals import app_authorized from oauth2_provider.views import TokenView, RevokeTokenView from rest_auth.serializers import PasswordChangeSerializer -from rest_framework import generics, viewsets, mixins +from rest_framework import generics, viewsets, mixins, views from rest_framework import status from rest_framework.decorators import ( api_view, @@ -39,7 +39,6 @@ from loppeonline.utils.mixins import ViewSetPagination from .models import CustomUser, Customer from .permissions import ( - IsAdminOwner, IsMarketAccess, IsMarketSalesAccess, IsMarketApiValid, @@ -322,16 +321,22 @@ def post(self, request, *args, **kwargs): return Response({"detail": _("New password has been saved.")}) -class UserRetrieveView(generics.RetrieveUpdateAPIView): +class UserRetrieveView(views.APIView): permission_classes = [IsAuthenticated, IsMarketAccess] - queryset = CustomUser.objects.all() serializer_class = CustomUserSerializer - lookup_field = 'username' - def perform_update(self, serializer): + def get(self, request): + serializer = CustomUserSerializer(request.user) + return Response(serializer.data) + + def put(self, request): + serializer = CustomUserSerializer( + instance=request.user, + data=request.data, + context={'request': request} + ) serializer.is_valid(raise_exception=True) updated_data = serializer.validated_data - serializer.save() email = updated_data.get('email', None) user = self.request.user @@ -351,7 +356,7 @@ def perform_update(self, serializer): # If email differs from initial user email (case sensitive) - save new email to DB user.email = email if send_email_reset: - user.verified = False + serializer.save(verified=False) send_email_reset_confirmation(user) # Logout user, as he changed their email @@ -364,13 +369,14 @@ def perform_update(self, serializer): request=self.request, ) - user.save() + return Response(serializer.validated_data) + serializer.save() + return Response(serializer.validated_data) class AdminRetrieveView(UserRetrieveView): permission_classes = [ IsAuthenticated, - IsAdminOwner, IsMarketAccess, IsMarketAdmin, ] @@ -385,23 +391,13 @@ class CustomerRetrieveView(UserRetrieveView): IsMarketCustomer, ] serializer_class = CustomerSerializer - lookup_field = 'user__email' - lookup_url_kwarg = 'email' - def get_queryset(self): - return Customer.objects.filter(market=self.request.user.market_inst) - - def get(self, request, email): - try: - instance = Customer.objects.get( - user__email__iexact=email, market=self.request.user.market_inst - ) - except Customer.DoesNotExist: - return Response( - {"error": _("Customer with such email does not exist.")} - ) + def get(self, request): + instance = Customer.objects.get( + user=request.user, market=self.request.user.market_inst + ) - serializer = self.get_serializer(instance) + serializer = CustomerSerializer(instance) return Response(serializer.data) diff --git a/loppeonline/settings/base.py b/loppeonline/settings/base.py index f26cef6c..d7e90f0f 100644 --- a/loppeonline/settings/base.py +++ b/loppeonline/settings/base.py @@ -414,9 +414,9 @@ # Password validation # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, + # { + # 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + # }, # Add them to make password validation stronger # { # 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', From 21024a931b1844f7920c99374159e3920ed213e5 Mon Sep 17 00:00:00 2001 From: dlenskyi Date: Thu, 15 Oct 2020 16:09:32 +0300 Subject: [PATCH 4/7] replace put to patch --- frontend/src-admin/services/index.js | 2 +- frontend/src-user/services/index.js | 2 +- loppeonline/apps/users/views.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src-admin/services/index.js b/frontend/src-admin/services/index.js index fe387738..1f9e2b1a 100644 --- a/frontend/src-admin/services/index.js +++ b/frontend/src-admin/services/index.js @@ -18,7 +18,7 @@ export const getAdmin = axios.create({ }) export const updateAdmin = axios.create({ - method: 'put', + method: 'patch', baseURL: `${userConfig.baseURL}/admin/`, headers: { 'Accept': 'application/json', diff --git a/frontend/src-user/services/index.js b/frontend/src-user/services/index.js index c0d4ef73..a2d96238 100644 --- a/frontend/src-user/services/index.js +++ b/frontend/src-user/services/index.js @@ -18,7 +18,7 @@ export const customerGet = axios.create({ }) export const updateCustomer = axios.create({ - method: 'put', + method: 'patch', baseURL: `${userConfig.baseURL}/customer/`, headers: { 'Accept': 'application/json', diff --git a/loppeonline/apps/users/views.py b/loppeonline/apps/users/views.py index ba776bcd..00693d8b 100644 --- a/loppeonline/apps/users/views.py +++ b/loppeonline/apps/users/views.py @@ -329,7 +329,7 @@ def get(self, request): serializer = CustomUserSerializer(request.user) return Response(serializer.data) - def put(self, request): + def patch(self, request): serializer = CustomUserSerializer( instance=request.user, data=request.data, From af4d936933ddca0f748403977e1fa324a71c6692 Mon Sep 17 00:00:00 2001 From: dlenskyi Date: Thu, 15 Oct 2020 16:19:51 +0300 Subject: [PATCH 5/7] updated docstring --- loppeonline/apps/users/permissions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/loppeonline/apps/users/permissions.py b/loppeonline/apps/users/permissions.py index 54ff5c09..4b8b68dc 100644 --- a/loppeonline/apps/users/permissions.py +++ b/loppeonline/apps/users/permissions.py @@ -12,7 +12,7 @@ def has_permission(self, request, view): class IsMarketAdmin(BasePermission): """ - Check if user instance has admin user type of + Check if the user is an admin """ def has_permission(self, request, view): @@ -21,7 +21,7 @@ def has_permission(self, request, view): class IsMarketCustomer(BasePermission): """ - Check if user instance has customer user type + Check if the user is a customer """ def has_permission(self, request, view): From 77b650830bf860170bc95f905c962442bfb826fc Mon Sep 17 00:00:00 2001 From: dlenskyi Date: Thu, 15 Oct 2020 16:28:30 +0300 Subject: [PATCH 6/7] updated customer retrieve get method --- loppeonline/apps/users/views.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/loppeonline/apps/users/views.py b/loppeonline/apps/users/views.py index 00693d8b..d6f3cfb1 100644 --- a/loppeonline/apps/users/views.py +++ b/loppeonline/apps/users/views.py @@ -393,11 +393,7 @@ class CustomerRetrieveView(UserRetrieveView): serializer_class = CustomerSerializer def get(self, request): - instance = Customer.objects.get( - user=request.user, market=self.request.user.market_inst - ) - - serializer = CustomerSerializer(instance) + serializer = CustomerSerializer(request.user.customer) return Response(serializer.data) From bdec7f350fa6cd5691ea47817787e610afb5d49f Mon Sep 17 00:00:00 2001 From: dlenskyi Date: Thu, 15 Oct 2020 16:38:27 +0300 Subject: [PATCH 7/7] optimized --- loppeonline/apps/users/views.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/loppeonline/apps/users/views.py b/loppeonline/apps/users/views.py index d6f3cfb1..649f4cd4 100644 --- a/loppeonline/apps/users/views.py +++ b/loppeonline/apps/users/views.py @@ -267,9 +267,9 @@ def dispatch(self, *args, **kwargs): return super(PasswordResetConfirmView, self).dispatch(*args, **kwargs) def post(self, request, *args, **kwargs): - res, reason = check_recaptcha(request) + result, reason = check_recaptcha(request) - if not res: + if not result: return Response( {'error': _('Recaptcha error: {}'.format(reason))}, status=HTTP_403_FORBIDDEN, @@ -571,7 +571,7 @@ def post(self, request, *args, **kwargs): required_params = ['username', 'password'] - if not all([r_param in data for r_param in required_params]): + if not all(r_param in data for r_param in required_params): return JsonResponse( {'error': _('Please provide both username and password')}, status=HTTP_400_BAD_REQUEST, @@ -620,9 +620,9 @@ def post(self, request, *args, **kwargs): # Check recaptcha only for admins if user.is_admin: - res, reason = check_recaptcha(data) + result, reason = check_recaptcha(data) - if not res: + if not result: return JsonResponse( {'error': _('Recaptcha error: {}'.format(reason))}, status=HTTP_403_FORBIDDEN, @@ -684,7 +684,7 @@ def post(self, request, *args, **kwargs): return super().post(request, *args, **kwargs) data = json.loads(body_unicode) - if not ('refresh_token' in data): + if 'refresh_token' not in data: return Response( {'error': _('Please provide refresh_token')}, status=HTTP_400_BAD_REQUEST,