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.
38 lines
1.4 KiB
Python
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)
|