From 89078bc9ba0ff65a0a6629b698355d1661591627 Mon Sep 17 00:00:00 2001 From: Khavin Shankar Date: Fri, 17 May 2024 19:51:47 +0530 Subject: [PATCH] make rate limit messages user friendly (#2174) --- care/abdm/api/viewsets/consent.py | 27 ++++-- care/abdm/api/viewsets/health_information.py | 12 ++- care/abdm/api/viewsets/healthid.py | 87 ++++++++++++++++---- care/abdm/api/viewsets/patients.py | 7 +- config/ratelimit.py | 24 ++++++ 5 files changed, 128 insertions(+), 29 deletions(-) diff --git a/care/abdm/api/viewsets/consent.py b/care/abdm/api/viewsets/consent.py index 4a695ada2a..eda7b2f47b 100644 --- a/care/abdm/api/viewsets/consent.py +++ b/care/abdm/api/viewsets/consent.py @@ -16,7 +16,7 @@ from care.utils.queryset.facility import get_facility_queryset from config.auth_views import CaptchaRequiredException from config.authentication import ABDMAuthentication -from config.ratelimit import ratelimit +from config.ratelimit import USER_READABLE_RATE_LIMIT_TIME, ratelimit logger = logging.getLogger(__name__) @@ -62,7 +62,10 @@ def create(self, request): request, "consent__create", [serializer.validated_data["patient_abha"]] ): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -91,7 +94,10 @@ def create(self, request): def status(self, request, pk): if ratelimit(request, "consent__status", [pk]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -112,7 +118,10 @@ def status(self, request, pk): def fetch(self, request, pk): if ratelimit(request, "consent__fetch", [pk]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -134,7 +143,10 @@ def fetch(self, request, pk): def list(self, request, *args, **kwargs): if ratelimit(request, "consent__list", [request.user.username]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -143,7 +155,10 @@ def list(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs): if ratelimit(request, "consent__retrieve", [kwargs["pk"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) diff --git a/care/abdm/api/viewsets/health_information.py b/care/abdm/api/viewsets/health_information.py index a1b33b4476..f6233b026e 100644 --- a/care/abdm/api/viewsets/health_information.py +++ b/care/abdm/api/viewsets/health_information.py @@ -14,7 +14,7 @@ from care.facility.models.file_upload import FileUpload from config.auth_views import CaptchaRequiredException from config.authentication import ABDMAuthentication -from config.ratelimit import ratelimit +from config.ratelimit import USER_READABLE_RATE_LIMIT_TIME, ratelimit logger = logging.getLogger(__name__) @@ -25,7 +25,10 @@ class HealthInformationViewSet(GenericViewSet): def retrieve(self, request, pk): if ratelimit(request, "health_information__retrieve", [pk]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -66,7 +69,10 @@ def retrieve(self, request, pk): def request(self, request, pk): if ratelimit(request, "health_information__request", [pk]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) diff --git a/care/abdm/api/viewsets/healthid.py b/care/abdm/api/viewsets/healthid.py index 318fa7e3b7..3bf3ac8ade 100644 --- a/care/abdm/api/viewsets/healthid.py +++ b/care/abdm/api/viewsets/healthid.py @@ -30,7 +30,7 @@ from care.facility.models.patient import PatientConsultation, PatientRegistration from care.utils.queryset.patient import get_patient_queryset from config.auth_views import CaptchaRequiredException -from config.ratelimit import ratelimit +from config.ratelimit import USER_READABLE_RATE_LIMIT_TIME, ratelimit logger = logging.getLogger(__name__) @@ -53,7 +53,10 @@ def generate_aadhaar_otp(self, request): if ratelimit(request, "generate_aadhaar_otp", [data["aadhaar"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -75,7 +78,10 @@ def resend_aadhaar_otp(self, request): if ratelimit(request, "resend_aadhaar_otp", [data["txnId"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -97,7 +103,10 @@ def verify_aadhaar_otp(self, request): if ratelimit(request, "verify_aadhaar_otp", [data["txnId"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -121,7 +130,10 @@ def generate_mobile_otp(self, request): if ratelimit(request, "generate_mobile_otp", [data["txnId"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -143,7 +155,10 @@ def verify_mobile_otp(self, request): if ratelimit(request, "verify_mobile_otp", [data["txnId"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -207,7 +222,10 @@ def create_health_id(self, request): if ratelimit(request, "create_health_id", [data["txnId"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -269,7 +287,10 @@ def search_by_health_id(self, request): request, "search_by_health_id", [data["healthId"]], increment=False ): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -284,7 +305,10 @@ def get_abha_card(self, request): if ratelimit(request, "get_abha_card", [data["patient"]], increment=False): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -320,7 +344,10 @@ def link_via_qr(self, request): if ratelimit(request, "link_via_qr", [data["hidn"]], increment=False): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -410,7 +437,10 @@ def get_new_linking_token(self, request): if ratelimit(request, "get_new_linking_token", [data["patient"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -448,7 +478,10 @@ def add_care_context(self, request, *args, **kwargs): if ratelimit(request, "add_care_context", [consultation_id]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -499,7 +532,10 @@ def patient_sms_notify(self, request, *args, **kwargs): if ratelimit(request, "patient_sms_notify", [patient_id]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -545,7 +581,10 @@ def auth_init(self, request): if ratelimit(request, "auth_init", [data["healthid"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -567,7 +606,10 @@ def confirm_with_aadhaar_otp(self, request): if ratelimit(request, "confirm_with_aadhaar_otp", [data["txnId"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -620,7 +662,10 @@ def confirm_with_mobile_otp(self, request): if ratelimit(request, "confirm_with_mobile_otp", [data["txnId"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -672,7 +717,10 @@ def confirm_with_demographics(self, request): if ratelimit(request, "confirm_with_demographics", [data["txnId"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) @@ -696,7 +744,10 @@ def check_and_generate_mobile_otp(self, request): if ratelimit(request, "check_and_generate_mobile_otp", [data["txnId"]]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) diff --git a/care/abdm/api/viewsets/patients.py b/care/abdm/api/viewsets/patients.py index 2bed1f63ee..267679d48d 100644 --- a/care/abdm/api/viewsets/patients.py +++ b/care/abdm/api/viewsets/patients.py @@ -13,7 +13,7 @@ from care.utils.notification_handler import send_webpush from config.auth_views import CaptchaRequiredException from config.authentication import ABDMAuthentication -from config.ratelimit import ratelimit +from config.ratelimit import USER_READABLE_RATE_LIMIT_TIME, ratelimit class PatientsViewSet(GenericViewSet): @@ -25,7 +25,10 @@ def find(self, request): if ratelimit(request, "patients__find", [identifier]): raise CaptchaRequiredException( - detail={"status": 429, "detail": "Too Many Requests Provide Captcha"}, + detail={ + "status": 429, + "detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}", + }, code=status.HTTP_429_TOO_MANY_REQUESTS, ) diff --git a/config/ratelimit.py b/config/ratelimit.py index 9ba26a2704..4f2594e339 100644 --- a/config/ratelimit.py +++ b/config/ratelimit.py @@ -56,3 +56,27 @@ def ratelimit( return False return False + + +def get_user_readable_rate_limit_time(rate_limit): + if not rate_limit: + return "1 second" + + requests, time = rate_limit.split("/") + + time_unit_map = { + "s": "second(s)", + "m": "minute(s)", + "h": "hour(s)", + "d": "day(s)", + } + + time_value = time[:-1] + time_unit = time[-1] + + return f"{time_value or 1} {time_unit_map.get(time_unit, 'second(s)')}" + + +USER_READABLE_RATE_LIMIT_TIME = get_user_readable_rate_limit_time( + settings.DJANGO_RATE_LIMIT +)