from django.http import HttpResponseForbidden
from django.conf import settings
class RestrictMiddleware(object):
"""
This middleware rejects certain requests depending on
four tests which are detailed in this classes'
`process_request` method.
All parameters for the operation of this middleware should
be defined in your settings.py file.
Adding this middleware to your project without specifying
any parameters will result in a default policy of all
requests being denied.
Valid settings options follow:
RESTRICT_IP_WHITELIST: a list or tuple of strings representing
IP addresses
RESTRICT_REFERRER_WHITELIST: a list or tuple of strings to be
searched as substrings in a request's HTTP_REFERRER
RESTRICT_GET_CODE: a dictionary of key/value pairs which, if
specified, will be checked for complete equality with a
client's GET paramters.
RESTRICT_COOKIE_NAME: a string representing the name of a cookie
that will be checked at the beginning of the process and will be
set if any of the tests pass.
RESTRICT_ACCESS_DENIED_MESSAGE: a string that will be displayed
to the user in the event of no tests being passed.
"""
IP_WHITELIST = []
REFERRER_WHITELIST = []
IP_BLACKLIST = []
GET_CODE = {}
COOKIE_NAME = 'auth'
ACCESS_DENIED_MESSAGE = 'Access Denied'
def process_request(self, request):
"""
Processes the HTTP request against a number of tests to determine
if access should be granted.
First, if the user has a particular cookie set to 'True', they
are passed through without further tests.
Second, the client's IP address is checked against an IP whitelist.
Third, the client's request's GET paramters are checked against
an optional set of key value pairs.
Fourth, the client's HTTP referrer is checked against a referrer
whitelist.
The user is considered valid if any one of the tests are evaluated
to be true.
If none of the tests pass, the user's request is rejected.
"""
cn = getattr(settings, 'RESTRICT_COOKIE_NAME', self.COOKIE_NAME)
if not request.session.get(cn, False):
ip = request.META['REMOTE_ADDR']
referrer = request.META.get('HTTP_REFERER', None)
ipw = getattr(settings, 'RESTRICT_IP_WHITELIST', self.IP_WHITELIST)
if (ip in ipw or self.valid_host(request, referrer)):
request.session[cn] = True
else:
return self.reject_request()
return None
def valid_host(self, request, ref):
"""
Checks the the RESTRICT_GET_CODE. It also checks the
referrer against a whitelist.
Accepts:
* request: this is a Django request object
* ref: the client's referrer string
Returns:
* True if the client is valid
* False if the client is not valid
Validity in this method is defined by passing one of the
following two tests.
There can be anywhere from 0 to n get codes which are key-
value pairs. These are checked against any GET parameters.
If any get codes are specified, all must be present and
both key and value must match. If there is a complete
match, then this function is short circuited and no
other values (such as referrer) are checked.
If there were no get codes specified, or if they did
not match completely, the referrer is checked against
a list of valid referrers. If any of the strings in the
RESTRICT_REFERRER_WHITELIST are found in any substring of
the client's referrer, then they are passed on as valid.
"""
### In our particular use case, the get code trumps all
g = None
gc = getattr(settings, 'RESTRICT_GET_CODE', self.GET_CODE)
if gc:
for parameter in gc.keys():
v = request.GET.get(parameter, None)
if v and v == gc[parameter]:
g = True
else:
g = False
if g != None and g == True:
return True
## If they didn't have all the get codes, we check referrer
rw = getattr(settings, 'RESTRICT_REFERRER_WHITELIST', self.REFERRER_WHITELIST)
if ref:
if rw:
for host in rw:
if (host.lower() in ref.lower()) or (ref.lower() in host.lower()):
return True
return False
def reject_request(self):
"""
Determines which user-facing message to display, and then
returns an HTTP Forbidden response along with that message.
"""
m = getattr(settings, 'RESTRICT_ACCESS_DENIED_MESSAGE', self.ACCESS_DENIED_MESSAGE)
return HttpResponseForbidden(m)