import uuid import structlog from django.core.exceptions import PermissionDenied from django.http import Http404 from ipware import get_client_ip from structlog.threadlocal import bind_threadlocal, clear_threadlocal from vbv_lernwelt.core.models import SecurityRequestResponseLog logger = structlog.get_logger(__name__) class GetIpBehindReverseProxyMiddleWare: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): client_ip, _is_routable = get_client_ip(request) request.META["REMOTE_ADDR"] = client_ip response = self.get_response(request) return response class SecurityRequestResponseLoggingMiddleware: def __init__(self, get_response): self.get_response = get_response def create_logging_threadlocalbind(self, request): request_username = request.user.username if hasattr(request, "user") else "" bind_threadlocal( request_method=request.method, request_full_path=request.get_full_path(), request_username=request_username, request_client_ip=request.META.get("REMOTE_ADDR"), request_trace_id=uuid.uuid4().hex, ) def create_database_security_request_response_log(self, request, response): try: entry = SecurityRequestResponseLog() entry.label = getattr(request, "security_request_logging", "") entry.request_method = request.method entry.request_full_path = request.get_full_path()[:255] entry.request_username = ( request.user.username if hasattr(request, "user") else "" ) entry.request_client_ip = request.META.get("REMOTE_ADDR") entry.request_scn = getattr(request, "scn", "") entry.response_status_code = response.status_code entry.additional_json_data = getattr( request, "log_additional_json_data", {} ) entry.save() # pylint: disable=broad-except except Exception: logger.warn("could not create db entry", label="security", exc_info=True) def log_request_response(self, request): clear_threadlocal() self.create_logging_threadlocalbind(request) logger.info( "url access initialized", label="security", ) response = self.get_response(request) security_request_logging = getattr(request, "security_request_logging", None) if security_request_logging: self.create_database_security_request_response_log(request, response) logger.info( "url access finished", label="security", response_status_code=response.status_code, request_ratelimited=getattr(request, "limited", False), request_finished=True, ) clear_threadlocal() return response def __call__(self, request): return self.log_request_response(request) def process_exception(self, request, exception): if isinstance(exception, (Http404, PermissionDenied)): # We don't log an exception here, and we don't set that we handled # an error as we want the standard `request_finished` log message # to be emitted. return self._raised_exception = True logger.exception( "request_failed", label="security", response_status_code=500, )