ixg_platform/apps/users/views.py
ixgadmin 8ad83a7a65 Restrict every data API to ops and admin
Batch writes were closed in 88eede5, but every other app's viewset still used
IsAuthenticated, which in this platform means any buyer. A buyer could rewrite a
document record, create a QC result (which moves the batch status and raises a
CAPA), and reach the write endpoints for CAPAs, certificates, ESG reports and
shipments.

- Use the one IsOpsOrAdmin from apps.compliance.permissions; drop the duplicate
  copy in apps/qc/views.py.
- Apply it to the whole viewset on shipments, qc, capa, esg, documents,
  certificates-of-analysis and regulatory-certs. The buyer portal is
  server-rendered through the ORM, so it needs none of these APIs.
- Keep the buyer chat working: AI interactions stay authenticated and scoped,
  and agent reads stay open while agent writes become ops/admin.
- Close a buyer-to-admin escalation in the users API: writes are admin-only and
  role/is_active are read-only, so a buyer can no longer PATCH their own row to
  role=admin and then pass every IsOpsOrAdmin check.
2026-09-17 15:53:44 +00:00

38 lines
1.4 KiB
Python

from rest_framework import viewsets, mixins, permissions
from apps.compliance.permissions import IsAdmin
from .models import User
from .serializers import UserSerializer, UserDetailSerializer, UserCreateSerializer
class UserViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
# A signed-in user may read their own record (get_queryset scopes a
# non-admin to it) but must not be able to write one. Without this, a buyer
# could PATCH their own row and set role to "admin", which would then pass
# every IsOpsOrAdmin check on the platform. Role and is_active are read-only
# in the serializer as well, so no API caller can change them.
permission_classes = [permissions.IsAuthenticated]
def get_permissions(self):
if self.action in ("create", "update", "partial_update", "destroy"):
return [IsAdmin()]
return super().get_permissions()
def get_serializer_class(self):
if self.action == "retrieve":
return UserDetailSerializer
if self.action == "create":
return UserCreateSerializer
return UserSerializer
def get_queryset(self):
user = self.request.user
if user.role == "admin":
return User.objects.all()
return User.objects.filter(id=user.id)