bgneal@472: """ bgneal@472: This module contains decorators for the antispam application. bgneal@472: bgneal@472: """ bgneal@472: from datetime import timedelta bgneal@472: from functools import wraps bgneal@472: bgneal@472: from django.shortcuts import render bgneal@472: bgneal@473: from antispam.rate_limit import RateLimiter, RateLimiterUnavailable bgneal@472: bgneal@472: bgneal@472: def rate_limit(count=10, interval=timedelta(minutes=1), bgneal@472: lockout=timedelta(hours=8)): bgneal@472: bgneal@472: def decorator(fn): bgneal@472: bgneal@472: @wraps(fn) bgneal@472: def wrapped(request, *args, **kwargs): bgneal@472: bgneal@473: ip = request.META.get('REMOTE_ADDR') bgneal@473: try: bgneal@473: rate_limiter = RateLimiter(ip, count, interval, lockout) bgneal@479: if rate_limiter.is_blocked(): bgneal@479: return render(request, 'antispam/blocked.html', status=403) bgneal@479: bgneal@473: except RateLimiterUnavailable: bgneal@473: # just call the function and return the result bgneal@473: return fn(request, *args, **kwargs) bgneal@473: bgneal@472: response = fn(request, *args, **kwargs) bgneal@472: bgneal@472: if request.method == 'POST': bgneal@472: success = (response and response.has_header('location') and bgneal@472: response.status_code == 302) bgneal@479: try: bgneal@479: if not success and rate_limiter.incr(): bgneal@479: return render(request, 'antispam/blocked.html', status=403) bgneal@479: bgneal@479: except RateLimiterUnavailable: bgneal@479: pass bgneal@472: bgneal@472: return response bgneal@472: bgneal@472: return wrapped bgneal@472: return decorator