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
- 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 hitslimit (
int
) – Allowed number of hits in the given time windowsetting (
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