websauna.system.form.throttle module

Deform throttling support.

websauna.system.form.throttle.clear_throttle(request, key_name)[source]

Clear the throttling status.

Example:

clear_throttle(request, "new-phone-number")
websauna.system.form.throttle.create_throttle_validator(name, max_actions_in_time_window, time_window_in_seconds=3600)[source]

Creates a Colander form validator which prevents form submissions exceed certain rate.

The benefit of using validator instead of throttle_view() decorator is that we can give a nice Colander form error message instead of an error page.

Form submissions are throttled system wide. This prevents abuse of the system by flooding it with requests.

A logging warning is issued if the rate is exceeded. The user is greeted with an error message telling the submission is not possible at the moment.

Example:

from tomb_routes import simple_route

from websauna.system.form.throttle import create_throttle_validator

from myapp import schemas

@simple_route("/login", route_name="login", renderer="login/login.html", append_slash=False)
def login(request):

    # Read allowed email login rate from the config file
    email_login_rate = int(request.registry.settings.get("trees.email_login_rate", 50))

    # Create a Colander schema instance with rate limit validator
    email_schema = schemas.LoginWithEmail(validator=create_throttle_validator("email_login", email_login_rate)).bind(request=request)
Parameters
  • name (str) – Identify this throttler. Used as a Redis key.

  • max_actions_in_time_window (int) – Number of allowed actions per window

  • time_window_in_seconds (int) – Time in window in seconds. Default one hour, 3600 seconds.

Returns

Function to be passed to validator Colander schema construction parameter.

websauna.system.form.throttle.throttled_view(rolling_window_id=None, time_window_in_seconds=3600, limit=50, setting=None)[source]

Decorate a view to protect denial-of-service attacks using throttling.

If the global throttling limit is exceeded the client gets HTTP 429 error.

Internally we use websauna.system.form.rollingwindow hit counting implementation.

Example that allows 30 page loads per hour:

@view_config(
    name="new-phone-number",
    renderer="wallet/new_phone_number.html",
    decorator=throttled_view(limit=30, time_window_in_seconds=3600))
def new_phone_number(request):
    # ... code goes here ...

You can also configure the limit in a INI settings file.

Example production.ini:

magiclogin.email_throttle = 50/3600
@view_config(
    name="email-login",
    renderer="wallet/new_phone_number.html",
    decorator=throttled_view(setting="magiclogin.email_throttle")
def new_phone_number(request):
    # ... code goes here ...
Parameters
  • rolling_window_id (Optional[str]) – The Redis key name used to store throttle state. If not given a key derived from view name is used.

  • time_window_in_seconds (int) – Sliding time window for counting hits

  • limit (int) – Allowed number of hits in the given time window

  • setting (Optional[str]) – Read throttle information from a INI settings. In this case string must be in format limit/time_window_seconds. Example: 50/3600. This overrides any given time and limit argument.

Raise

pyramid.httpexceptions.HTTPTooManyRequests if the endpoint gets hammered too much