Files
customerTicket/apps/payments/middleware.py
T
2026-10-06 10:03:22 +03:30

54 lines
2.4 KiB
Python

"""Audit administrative financial grant changes with the actual request actor.
Only selected non-secret fields and payments permissions are captured."""
from django.apps import apps
from django.db import OperationalError, ProgrammingError
from .models import PaymentAuditEvent
TARGETS = {'accounts/user': 'accounts.User', 'auth/group': 'auth.Group',
'staff/organizationalunit': 'staff.OrganizationalUnit', 'staff/unitmembership': 'staff.UnitMembership',
'staff/staffprofile': 'staff.StaffProfile'}
def access_snapshot(obj):
fields = ['is_active','account_type','user_id','unit_id','ticket_scope','role','primary_unit_id']
result = {field:getattr(obj,field) for field in fields if hasattr(obj,field)}
for name in ['permissions','user_permissions']:
relation = getattr(obj,name,None)
if relation is not None:
result[name] = sorted(relation.filter(content_type__app_label='payments').values_list('codename',flat=True))
if hasattr(obj,'groups'):
result['groups'] = sorted(obj.groups.filter(permissions__content_type__app_label='payments').values_list('pk',flat=True).distinct())
return result
class FinancialGrantAuditMiddleware:
def __init__(self,get_response):
self.get_response=get_response
def __call__(self,request):
target = self.target(request)
before = access_snapshot(target) if target else None
response = self.get_response(request)
if target and response.status_code < 400:
try:
target.refresh_from_db()
after=access_snapshot(target)
if before != after:
from .services import audit
audit(request.user,'FINANCIAL_ACCESS_CHANGED',target,{'before':before,'after':after})
except target.DoesNotExist:
from .services import audit
audit(request.user,'FINANCIAL_ACCESS_REMOVED',target,{'before':before})
return response
def target(self,request):
if request.method != 'POST' or not request.user.is_authenticated or not request.path.startswith('/x5admin/'):
return None
parts=request.path.strip('/').split('/')
if len(parts)<5 or not parts[3].isdecimal():
return None
label=TARGETS.get('/'.join(parts[1:3]))
if not label:
return None
return apps.get_model(label).objects.filter(pk=parts[3]).first()