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@505: from django.utils import simplejson
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@505: 
bgneal@505:                 # Figure out if the view succeeded; if it is a non-ajax view,
bgneal@505:                 # then success means a redirect is about to occur. If it is
bgneal@505:                 # an ajax view, we have to decode the json response.
bgneal@505:                 success = False
bgneal@505:                 if not request.is_ajax():
bgneal@505:                     success = (response and response.has_header('location') and
bgneal@505:                             response.status_code == 302)
bgneal@505:                 elif response:
bgneal@505:                     json_resp = simplejson.loads(response.content)
bgneal@505:                     success = json_resp['success']
bgneal@505: 
bgneal@505:                 if not success:
bgneal@505:                     try:
bgneal@505:                         blocked = rate_limiter.incr()
bgneal@505:                     except RateLimiterUnavailable:
bgneal@505:                         blocked = False
bgneal@505: 
bgneal@505:                     if blocked:
bgneal@479:                         return render(request, 'antispam/blocked.html', status=403)
bgneal@479: 
bgneal@472:             return response
bgneal@472: 
bgneal@472:         return wrapped
bgneal@472:     return decorator