diff --git a/.gitignore b/.gitignore index d96bde8..f9979e8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ delme*.py .env /venv/ oidc.key +/log/ /logs/*.log diff --git a/apps/users/models.py b/apps/users/models.py index 0ab60af..cc1ac4d 100644 --- a/apps/users/models.py +++ b/apps/users/models.py @@ -160,9 +160,6 @@ class User(AbstractUser): return state.get_province_by_id(self.province) def set_otp(self): - if self.otp_expire and (self.otp_expire + timedelta(seconds=MAX_OTP_VALID_DURATION)) > timezone.now(): - raise Exception(_('otp expire time not reached.')) - if not settings.SMS_SEND: self._otp = '77501' elif self.phone_number in settings.TEST_PHONENUMBERS: diff --git a/apps/users/serializers.py b/apps/users/serializers.py index 82e13be..a5f0cc2 100644 --- a/apps/users/serializers.py +++ b/apps/users/serializers.py @@ -76,16 +76,9 @@ class RequestOTPSerializer(serializers.ModelSerializer): user = User.objects.create_user(**validated_data) if not user.otp_is_valid(): - try: - user.set_otp() - except Exception as e: - raise UnprocessableEntity(_('otp expire time not reached')) - + user.set_otp() user.send_otp() - else: - raise UnprocessableEntity(_('otp expire time not reached yet')) - self.instance = user return user diff --git a/apps/users/views.py b/apps/users/views.py index 9607ec6..ef6b6f0 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -21,7 +21,7 @@ from apps.users.models import User from apps.users.provinces_and_cities import State from apps.users.serializers import PublicUserSerializer, AccountSerializer, RequestOTPSerializer, RequestOTTSerializer, \ ChangePasswordSerializer, UserInquirySerializer, SessionSerializer -from utils.throttles import RequestOTPDayRateThrottle, RequestOTPMinRateThrottle +from utils.throttles import RequestOTPDayRateThrottle, RequestOTPMinRateThrottle, NumberedRequestOTPDayRateThrottle, NumberedRequestOTPMinRateThrottle UserModel = get_user_model() @@ -90,7 +90,10 @@ class RequestOTPView(generics.CreateAPIView): permission_classes = [] serializer_class = RequestOTPSerializer required_scopes = [] - throttle_classes = [RequestOTPMinRateThrottle, RequestOTPDayRateThrottle] + throttle_classes = [RequestOTPMinRateThrottle, + RequestOTPDayRateThrottle, + NumberedRequestOTPDayRateThrottle, + NumberedRequestOTPMinRateThrottle] class RequestOTTView(generics.CreateAPIView): permission_classes = [IsAuthenticatedOrTokenHasScope] diff --git a/client.py b/client.py index 57e85d2..29a1577 100644 --- a/client.py +++ b/client.py @@ -3,10 +3,10 @@ import time import requests -OAUTH_CLIENT_ID = 'xrFXKf53jrxVOygbLEoqrtOlUwBP00jIQ4zVpzc6' -OAUTH_CLIENT_SECRET = 'qelUdRCdEEalVdko5aHRojxsC3CaL29yjwxmc6FeWs39SeVSb6aM9mNrYQixMJNTYQK7s2wffwPW94JPPbP6jTdd9dSf1mlqYcr6hW2COuayFUk4jP33OWu4taUYP2F5' +OAUTH_CLIENT_ID = '4INGOCMoulE0fNY1SQlTbPtsWqqxGj2DdqjADq6u' +OAUTH_CLIENT_SECRET = 'KPc4dMSztNwAIB3vii3geXrzC1mKUIsAztz3t2ylC3HlrgJudJWhdrtoY6XeRJIuadTAREYYsXk9XtFHUDfPVcJvyfHMpNkz2VvghwrijhVprok0VYV7XrOirJ5nFUxD' -API_URI = 'https://accounts.gooyal.com' +API_URI = 'https://accounts.gooyal.ir' # API_URI = 'http://127.0.0.1:8000' @@ -27,7 +27,7 @@ class ApiClient(): "grant_type": "password", "username": phone_number, "password": password, - "scope": 'accounts.account:request_ott accounts.account:change_password accounts.profile:inquiry accounts.account:update accounts.account:retrieve accounts.profile:retrieve accounts.profile:list introspection wallet.wallet:get_balance', + "scope": 'introspection', "auth_fields": 'phone_number:otp' } auth = (OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET) @@ -51,7 +51,8 @@ class ApiClient(): "grant_type": "password", "username": phone_number, "password": token, - "scope": 'introspection education.course:retrieve education.course:submit superapp.applications:list accounts.account:request_ott accounts.profile:list accounts.profile:retrieve accounts.account:retrieve accounts.account:update accounts.profile:inquiry accounts.account:change_password wallet.transaction:list wallet.invoice:create wallet.transaction:retrieve wallet.invoice:pay wallet.invoice:receipt wallet.deposit:submit wallet.deposit:verify wallet.withdraw:submit wallet.withdraw:verify ', + # "scope": 'introspection education.course:retrieve education.course:submit superapp.applications:list accounts.account:request_ott accounts.profile:list accounts.profile:retrieve accounts.account:retrieve accounts.account:update accounts.profile:inquiry accounts.account:change_password wallet.transaction:list wallet.invoice:create wallet.transaction:retrieve wallet.invoice:pay wallet.invoice:receipt wallet.deposit:submit wallet.deposit:verify wallet.withdraw:submit wallet.withdraw:verify ', + "scope": 'introspection', "auth_fields": 'phone_number:ott' } auth = (OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET) @@ -156,6 +157,7 @@ class ApiClient(): } url = f'{API_URI}/{path}' response = self._request(url, method='PUT', data=data) + print(response) return response and 'code' in response, response def introspect_account(self, token): @@ -185,19 +187,29 @@ class ApiClient(): return self._request(url=url) - def get_wallet_balance(self): - url = 'http://127.0.0.1:8000/wallet/api/wallet/default/balance' + def get_application_preferences(self): + url = 'https://preferences.gooyal.com/data/application/' return self._request(url=url) +auth_data = {'access_token': 'DJ1mycH9r5FCwaNgDA9nB9uU3KWt9m', 'expires_in': 36000, 'token_type': 'Bearer', 'scope': 'accounts.account:change_password accounts.account:update accounts.account:retrieve wallet.wallet:get_balance billboard_merchant.billboard:retrieve billboard_merchant.billboard:update billboard_merchant.billboard:create wallet.user:transaction_list data_crud.data:retrieve data_crud.data:update data_crud.data:delete', 'refresh_token': 'VF4P11tfcnGv9tc2u0qH6035OW5qU4'} + client = ApiClient('+989106853582') +# client.auth_data = auth_data # print(client.login_as_client_credentials()) print(client.request_otp()) -print(client.otp_login('12345')) -print(client.introspect_account('V3LUQ9OryHOy6q5iWwLBRAtRBm80ex')) -print(client.get_wallet_balance()) +import time +start = time.time() +print(client.otp_login('77502')) +# end = time.time() +# print(end - start) +# print(client.get_application_preferences()) +# exit() +# print(client.introspect_account('V3LUQ9OryHOy6q5iWwLBRAtRBm80ex')) +# print(client.get_wallet_balance()) # ott = client.get_application_token()[1]['ott'] # print(client.ott_login(ott)) # print(ott) +client.update_account('test') diff --git a/main/celery.py b/main/celery.py index 6b7fc88..697b017 100644 --- a/main/celery.py +++ b/main/celery.py @@ -6,7 +6,7 @@ # from django.conf import settings # # # set the default Django settings module for the 'celery' program. -# # os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'accounts.settings') +# # os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'main.settings') # # app = Celery('notification_service') # diff --git a/main/settings.py b/main/settings.py index 3dd9f8f..fcf4d23 100644 --- a/main/settings.py +++ b/main/settings.py @@ -126,7 +126,9 @@ REST_FRAMEWORK = { ], 'DEFAULT_THROTTLE_RATES': { 'otp_min': '2/min', - 'otp_day': '200/day', + 'otp_day': '50/day', + 'numbered_otp_min': '1/min', + 'numbered_otp_day': '10/day', }, 'EXCEPTION_HANDLER': 'utils.exceptions.exception_handler', } @@ -293,7 +295,7 @@ CACHES = { } } - +LOKI_BASE_PUBLIC_URL = config('LOKI_BASE_PUBLIC_URL', default=None, cast=str) from main.other_settings.logging import LOGGING @@ -315,7 +317,7 @@ STORAGES = { "MINIO_ACCESS_KEY": config('MINIO_ACCESS_KEY'), "MINIO_SECRET_KEY": config('MINIO_SECRET_KEY'), "MINIO_URL_EXPIRY_HOURS": timedelta(days=1), # Default is 7 days (longest) if not defined - "MINIO_CONSISTENCY_CHECK_ON_START": False, + "MINIO_CONSISTENCY_CHECK_ON_START": True, "MINIO_DEFAULT_BUCKET": config('MINIO_MEDIA_FILES_BUCKET'), # replacement for MEDIA_ROOT # MINIO_STATIC_FILES_BUCKET = 'my-static-files-bucket' # replacement for STATIC_ROOT diff --git a/run.sh b/run.sh index 051a5d0..e24f8ab 100755 --- a/run.sh +++ b/run.sh @@ -4,6 +4,6 @@ # sleep 3 #done #python3 manage.py collectstatic --noinput -#python3 manage.py migrate +python3 manage.py migrate gunicorn main.wsgi:application --bind 0.0.0.0:8000 -w 4 -#daphne -b 0.0.0.0 -p 8000 accounts.asgi:application \ No newline at end of file +#daphne -b 0.0.0.0 -p 8000 main.asgi:application \ No newline at end of file diff --git a/utils/exceptions.py b/utils/exceptions.py index 0c6dd2e..8e730cd 100644 --- a/utils/exceptions.py +++ b/utils/exceptions.py @@ -23,14 +23,9 @@ def exception_handler(exc, context): if isinstance(exc, APIException): try: - if isinstance(exc.detail, dict): - if 'code' in exc.detail: - error = exc.detail['code'] - else: - attr = next(iter(exc.detail)) - error = exc.detail[attr].code - else: - error = exc.detail.code or exc.code + error = getattr(getattr(exc, 'detail', None), 'code', None) or getattr(exc, 'code', None) + if not error: + error = getattr(exc, 'default_code') except: error = '' @@ -46,10 +41,7 @@ def exception_handler(exc, context): # message = error # # response_data['message'] = message - if isinstance(exc.detail, dict): - response_data['details'] = exc.detail - else: - response_data['details'] = {"message": exc.detail} + response_data['details'] = {"message": exc.detail} response_data['details']['error'] = error response_data['details']['timestamp'] = timezone.now().isoformat() @@ -89,7 +81,7 @@ from django.utils.translation import gettext_lazy as _ class UnprocessableEntity(APIException): - status_code = 422 + status_code = status.HTTP_422_UNPROCESSABLE_ENTITY default_detail = 'The request was well-formed but cannot be processed due to semantic errors.' default_code = 'unprocessable_entity' diff --git a/utils/throttles.py b/utils/throttles.py index 33b9e9c..fb4f5ee 100644 --- a/utils/throttles.py +++ b/utils/throttles.py @@ -16,9 +16,33 @@ class RequestOTPDayRateThrottle(SimpleRateThrottle): } + class RequestOTPMinRateThrottle(RequestOTPDayRateThrottle): scope = 'otp_min' +class NumberedRequestOTPDayRateThrottle(SimpleRateThrottle): + scope = 'numbered_otp_day' + + def get_ident(self, request): + return request.data.get('phone_number') + + def get_cache_key(self, request, view): + if request.user and request.user.is_authenticated: + ident = request.user.pk + else: + ident = self.get_ident(request) + + return self.cache_format % { + 'scope': self.scope, + 'ident': ident + } + + + +class NumberedRequestOTPMinRateThrottle(NumberedRequestOTPDayRateThrottle): + scope = 'numbered_otp_min' + +