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.
35 lines
1.3 KiB
Python
35 lines
1.3 KiB
Python
from rest_framework import serializers
|
|
from .models import User
|
|
|
|
|
|
class UserSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = User
|
|
fields = ["id", "email", "full_name", "company_name", "role", "is_active", "created_at"]
|
|
# Role and is_active are never set through this API: a writable role is
|
|
# how a buyer could promote themselves to admin. They are changed in the
|
|
# administration area instead.
|
|
read_only_fields = ["id", "created_at", "role", "is_active"]
|
|
|
|
|
|
class UserDetailSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = User
|
|
fields = ["id", "email", "full_name", "company_name", "role", "stripe_customer_id", "is_active", "created_at", "updated_at"]
|
|
read_only_fields = ["id", "created_at", "updated_at"]
|
|
|
|
|
|
class UserCreateSerializer(serializers.ModelSerializer):
|
|
password = serializers.CharField(write_only=True, required=False)
|
|
|
|
class Meta:
|
|
model = User
|
|
fields = ["email", "full_name", "company_name", "role", "password"]
|
|
|
|
def create(self, validated_data):
|
|
password = validated_data.pop("password", None)
|
|
user = User.objects.create_user(**validated_data)
|
|
if password:
|
|
user.set_password(password)
|
|
user.save()
|
|
return user
|