refactor: allow callable limit arg for ratelimit deco
As we make all configurations editable through dashboard(ex: password_reset_limit), it makes sense to provide limit as a callable so that it can be accessed dynamically.
This commit is contained in:
parent
14b3d344a5
commit
de210260a7
4 changed files with 17 additions and 20 deletions
|
|
@ -2,21 +2,21 @@
|
|||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals, print_function
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint, flt, has_gravatar, escape_html, format_datetime, now_datetime, get_formatted_email, today
|
||||
from frappe import throw, msgprint, _
|
||||
from frappe.utils.password import update_password as _update_password, check_password
|
||||
from frappe.utils.password import update_password as _update_password, check_password, get_password_reset_limit
|
||||
from frappe.desk.notifications import clear_notifications
|
||||
from frappe.desk.doctype.notification_settings.notification_settings import create_notification_settings, toggle_notifications
|
||||
from frappe.utils.user import get_system_managers
|
||||
from bs4 import BeautifulSoup
|
||||
import frappe.permissions
|
||||
import frappe.share
|
||||
import frappe.defaults
|
||||
from frappe.website.utils import is_signup_enabled
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
from frappe.rate_limiter import rate_limit
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
|
||||
|
||||
STANDARD_USERS = ("Guest", "Administrator")
|
||||
|
||||
|
|
@ -838,7 +838,7 @@ def sign_up(email, full_name, redirect_to):
|
|||
return 2, _("Please ask your administrator to verify your sign-up")
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
@rate_limit(key='user', limit=3, seconds = 24*60*60, methods=['POST'])
|
||||
@rate_limit(key='user', limit=get_password_reset_limit, seconds = 24*60*60, methods=['POST'])
|
||||
def reset_password(user):
|
||||
if user=="Administrator":
|
||||
return 'not allowed'
|
||||
|
|
|
|||
|
|
@ -207,8 +207,7 @@ scheduler_events = {
|
|||
"frappe.deferred_insert.save_to_db",
|
||||
"frappe.desk.form.document_follow.send_hourly_updates",
|
||||
"frappe.integrations.doctype.google_calendar.google_calendar.sync",
|
||||
"frappe.email.doctype.newsletter.newsletter.send_scheduled_email",
|
||||
"frappe.utils.password.delete_password_reset_cache"
|
||||
"frappe.email.doctype.newsletter.newsletter.send_scheduled_email"
|
||||
],
|
||||
"daily": [
|
||||
"frappe.email.queue.set_expiry_for_email_queue",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||
|
||||
from datetime import datetime
|
||||
from functools import wraps
|
||||
from typing import Union
|
||||
from typing import Union, Callable
|
||||
|
||||
from werkzeug.wrappers import Response
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ class RateLimiter:
|
|||
if self.rejected:
|
||||
return Response(_("Too Many Requests"), status=429)
|
||||
|
||||
def rate_limit(key: str, limit: int = 5, seconds: int= 24*60*60, methods: Union[str, list]='ALL'):
|
||||
def rate_limit(key: str, limit: Union[int, Callable] = 5, seconds: int= 24*60*60, methods: Union[str, list]='ALL'):
|
||||
"""Decorator to rate limit an endpoint.
|
||||
|
||||
This will limit Number of requests per endpoint to `limit` within `seconds`.
|
||||
|
|
@ -92,6 +92,7 @@ def rate_limit(key: str, limit: int = 5, seconds: int= 24*60*60, methods: Union[
|
|||
|
||||
:param key: Key is used to identify the requests uniqueness
|
||||
:param limit: Maximum number of requests to allow with in window time
|
||||
:type limit: Callable or Integer
|
||||
:param seconds: window time to allow requests
|
||||
:param methods: Limit the validation for these methods.
|
||||
`ALL` is a wildcard that applies rate limit on all methods.
|
||||
|
|
@ -106,6 +107,8 @@ def rate_limit(key: str, limit: int = 5, seconds: int= 24*60*60, methods: Union[
|
|||
if methods != 'ALL' and frappe.request.method.upper() not in methods:
|
||||
return frappe.call(fun, **frappe.form_dict)
|
||||
|
||||
_limit = limit() if callable(limit) else limit
|
||||
|
||||
identity = frappe.form_dict[key]
|
||||
cache_key = f"rl:{frappe.form_dict.cmd}:{identity}"
|
||||
|
||||
|
|
@ -114,7 +117,7 @@ def rate_limit(key: str, limit: int = 5, seconds: int= 24*60*60, methods: Union[
|
|||
frappe.cache().set_value(cache_key, 0, expires_in_sec=seconds)
|
||||
|
||||
value = frappe.cache().incrby(cache_key, 1)
|
||||
if value > limit:
|
||||
if value > _limit:
|
||||
frappe.throw(_("You hit the rate limit because of too many requests. Please try after sometime."))
|
||||
|
||||
return frappe.call(fun, **frappe.form_dict)
|
||||
|
|
|
|||
|
|
@ -90,14 +90,6 @@ def delete_login_failed_cache(user):
|
|||
frappe.cache().hdel('login_failed_count', user)
|
||||
frappe.cache().hdel('locked_account_time', user)
|
||||
|
||||
|
||||
def delete_password_reset_cache(user=None):
|
||||
if user:
|
||||
frappe.cache().hdel('password_reset_link_count', user)
|
||||
else:
|
||||
frappe.cache().delete_key('password_reset_link_count')
|
||||
|
||||
|
||||
def update_password(user, pwd, doctype='User', fieldname='password', logout_all_sessions=False):
|
||||
'''
|
||||
Update the password for the User
|
||||
|
|
@ -179,3 +171,6 @@ def get_encryption_key():
|
|||
frappe.local.conf.encryption_key = encryption_key
|
||||
|
||||
return frappe.local.conf.encryption_key
|
||||
|
||||
def get_password_reset_limit():
|
||||
return frappe.db.get_single_value("System Settings", "password_reset_limit") or 0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue