Explorar el Código

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.
version-14
leela hace 4 años
padre
commit
de210260a7
Se han modificado 4 ficheros con 17 adiciones y 20 borrados
  1. +7
    -7
      frappe/core/doctype/user/user.py
  2. +1
    -2
      frappe/hooks.py
  3. +6
    -3
      frappe/rate_limiter.py
  4. +3
    -8
      frappe/utils/password.py

+ 7
- 7
frappe/core/doctype/user/user.py Ver fichero

@@ -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'


+ 1
- 2
frappe/hooks.py Ver fichero

@@ -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
- 3
frappe/rate_limiter.py Ver fichero

@@ -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)


+ 3
- 8
frappe/utils/password.py Ver fichero

@@ -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

Cargando…
Cancelar
Guardar