* chore: nginx log is s, not ms * chore: log seconds from gunicorn too * chore: drop X-Real-IP header / log * style: Black * style: single -> double quotes * feat: add is-authenticated header * feat: log is-authenticated header * chore: update nginx-auth.conf to match
102 lines
3.4 KiB
Python
102 lines
3.4 KiB
Python
# Copyright The IETF Trust 2007-2020, All Rights Reserved
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
from django.db import connection
|
|
from django.db.utils import OperationalError
|
|
from django.shortcuts import render
|
|
from django.http import HttpResponsePermanentRedirect
|
|
from ietf.utils.log import log, exc_parts
|
|
from ietf.utils.mail import log_smtp_exception
|
|
import re
|
|
import smtplib
|
|
import unicodedata
|
|
|
|
|
|
def sql_log_middleware(get_response):
|
|
def sql_log(request):
|
|
response = get_response(request)
|
|
for q in connection.queries:
|
|
if re.match("(update|insert)", q["sql"], re.IGNORECASE):
|
|
log(q["sql"])
|
|
return response
|
|
|
|
return sql_log
|
|
|
|
|
|
class SMTPExceptionMiddleware(object):
|
|
def __init__(self, get_response):
|
|
self.get_response = get_response
|
|
|
|
def __call__(self, request):
|
|
return self.get_response(request)
|
|
|
|
def process_exception(self, request, exception):
|
|
if isinstance(exception, smtplib.SMTPException):
|
|
(extype, value, tb) = log_smtp_exception(exception)
|
|
return render(
|
|
request,
|
|
"email_failed.html",
|
|
{"exception": extype, "args": value, "traceback": "".join(tb)},
|
|
)
|
|
return None
|
|
|
|
|
|
class Utf8ExceptionMiddleware(object):
|
|
def __init__(self, get_response):
|
|
self.get_response = get_response
|
|
|
|
def __call__(self, request):
|
|
return self.get_response(request)
|
|
|
|
def process_exception(self, request, exception):
|
|
if isinstance(exception, OperationalError):
|
|
extype, e, tb = exc_parts()
|
|
if e.args[0] == 1366:
|
|
log("Database 4-byte utf8 exception: %s: %s" % (extype, e))
|
|
return render(
|
|
request,
|
|
"utf8_4byte_failed.html",
|
|
{"exception": extype, "args": e.args, "traceback": "".join(tb)},
|
|
)
|
|
return None
|
|
|
|
|
|
def redirect_trailing_period_middleware(get_response):
|
|
def redirect_trailing_period(request):
|
|
response = get_response(request)
|
|
if response.status_code == 404 and request.path.endswith("."):
|
|
return HttpResponsePermanentRedirect(request.path.rstrip("."))
|
|
return response
|
|
|
|
return redirect_trailing_period
|
|
|
|
|
|
def unicode_nfkc_normalization_middleware(get_response):
|
|
def unicode_nfkc_normalization(request):
|
|
"""Do Unicode NFKC normalization to turn ligatures into individual characters.
|
|
This was prompted by somebody actually requesting an url for /wg/ipfix/charter
|
|
where the 'fi' was composed of an \\ufb01 ligature...
|
|
|
|
There are probably other elements of a request which may need this normalization
|
|
too, but let's put that in as it comes up, rather than guess ahead.
|
|
"""
|
|
request.META["PATH_INFO"] = unicodedata.normalize(
|
|
"NFKC", request.META["PATH_INFO"]
|
|
)
|
|
request.path_info = unicodedata.normalize("NFKC", request.path_info)
|
|
response = get_response(request)
|
|
return response
|
|
|
|
return unicode_nfkc_normalization
|
|
|
|
|
|
def is_authenticated_header_middleware(get_response):
|
|
"""Middleware to add an is-authenticated header to the response"""
|
|
def add_header(request):
|
|
response = get_response(request)
|
|
response["X-Datatracker-Is-Authenticated"] = "yes" if request.user.is_authenticated else "no"
|
|
return response
|
|
|
|
return add_header
|