"""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()