1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 19:56:48 +00:00

feat(logs): a refused page or API call leaves a Logs row

Each 401 or 403 answered to a signed-in user or to a request with
credentials (an API token) writes "Access denied (status): method path"
with the user and the address. Anonymous refusals are left out, so a
scanner cannot fill the table.
This commit is contained in:
catborise 2026-11-05 14:39:10 +03:00
parent bc79c0c88a
commit 9ac1a29516
4 changed files with 51 additions and 2 deletions

View file

@ -7,7 +7,7 @@ from django.shortcuts import redirect, render
from django.utils.http import url_has_allowed_host_and_scheme
from django.utils.translation import gettext_lazy as _
from libvirt import libvirtError
from logs.audit import request_ip
from logs.audit import request_ip, write
from operations import claims, jobs
from operations.utils import busy_message, public_error_text, said, summary, words
@ -63,6 +63,28 @@ class RequestIdMiddleware:
return response
class AccessDeniedMiddleware:
"""
A Logs row for each 401 or 403 answered to a signed-in user or to a request
with credentials (an API token). Anonymous requests are left out: a
scanner would fill the table.
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if response.status_code in (401, 403):
user = getattr(request, "user", None)
signed_in = user is not None and user.is_authenticated
if signed_in or "Authorization" in request.headers:
write(user.get_username() if signed_in else "", "-", "",
f"Access denied ({response.status_code}): {request.method} {request.path}", False,
request_ip(request))
return response
class ContentSecurityPolicyMiddleware:
def __init__(self, get_response):
self.get_response = get_response

View file

@ -88,6 +88,7 @@ INSTALLED_APPS = [
MIDDLEWARE = [
"webvirtcloud.middleware.RequestIdMiddleware",
"webvirtcloud.middleware.AccessDeniedMiddleware",
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",