From 66b95f6475eb7757688027c5b99f6bdab3e1c969 Mon Sep 17 00:00:00 2001 From: Flook Date: Sun, 9 Nov 2025 15:05:02 +0700 Subject: [PATCH] =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=A1?= =?UTF-8?q?=20RBAC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model_registry/views/ai_model_viewset.py | 17 ++++++ backend/permissions/permission_classes.py | 56 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 backend/permissions/permission_classes.py diff --git a/backend/model_registry/views/ai_model_viewset.py b/backend/model_registry/views/ai_model_viewset.py index 42bc7fd..7c1af5b 100644 --- a/backend/model_registry/views/ai_model_viewset.py +++ b/backend/model_registry/views/ai_model_viewset.py @@ -8,6 +8,9 @@ from ..serializers.ai_model_serializer import AiModelSerializer from ..repositories.ai_model_repository import AiModelRepository from ..services.ai_model_service import AiModelService, ConnectionError +from permissions.permission_classes import IsAdminOrManager +from rest_framework.permissions import IsAuthenticated + # Dependency Injection: สร้าง Instance ของ Repository และ Service # สำหรับโปรเจกต์ขนาดเล็กสามารถทำแบบนี้ได้ repo = AiModelRepository() @@ -44,6 +47,20 @@ class AiModelRegistryViewSet(viewsets.ModelViewSet): except Exception as e: return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) + def get_permissions(self): + # การทำงานที่เกี่ยวข้องกับการเขียน/แก้ไข/ลบ ถือเป็นงาน Admin/Manager + if self.action in ['create', 'update', 'partial_update', 'destroy', 'set_status']: + # จำกัดสิทธิ์ ต้องเป็น Admin หรือ Manager เท่านั้น + return [IsAdminOrManager()] + + # การทำงานที่เกี่ยวข้องกับการอ่านข้อมูล (List, Retrieve, Test Connection) + # ถือเป็นงานสำหรับผู้ใช้งานที่ล็อกอินแล้ว (Viewer ขึ้นไป) + elif self.action in ['list', 'retrieve', 'test_connection']: + return [IsAuthenticated()] + + # Default สำหรับเมธอดอื่น ๆ หรือ Custom Action ที่ไม่ได้ระบุ + return [IsAuthenticated()] + # ----------------------------------------------- # Custom Action: ทดสอบการเชื่อมต่อ # ----------------------------------------------- diff --git a/backend/permissions/permission_classes.py b/backend/permissions/permission_classes.py new file mode 100644 index 0000000..d67c4e6 --- /dev/null +++ b/backend/permissions/permission_classes.py @@ -0,0 +1,56 @@ +from rest_framework import permissions +from rest_framework.exceptions import PermissionDenied + + +# ---------------------------------------------------- +# Base Class เพื่อใช้ Logic การตรวจสอบสิทธิ์ร่วมกัน +# ---------------------------------------------------- +class BaseRolePermission(permissions.BasePermission): + """ + Base class สำหรับตรวจสอบสิทธิ์ตาม is_superuser และ is_staff + """ + def is_admin(self, user): + return user.is_authenticated and user.is_superuser + + def is_manager(self, user): + # ผู้จัดการคือ is_staff แต่ไม่ใช่ superuser + return user.is_authenticated and user.is_staff and not user.is_superuser + + # หรือเพิ่มฟังก์ชัน is_model_owner(user) ถ้าจำเป็น + # ถ้าใช้ฟิลด์ 'role' ที่คุณเพิ่มใน Serializer ควรใช้: + # return user.is_authenticated and getattr(user, 'role', '').upper() == 'ADMIN' + + +# ---------------------------------------------------- +# 1. Permission: อนุญาตเฉพาะ Admin เท่านั้น +# ---------------------------------------------------- +class IsAdmin(BaseRolePermission): + message = "คุณไม่มีสิทธิ์เข้าถึงหน้านี้ (Admin Required)." + + def has_permission(self, request, view): + # Admin คือ is_superuser + return self.is_admin(request.user) + + +# ---------------------------------------------------- +# 2. Permission: อนุญาตเฉพาะ Admin หรือ Manager เท่านั้น +# ---------------------------------------------------- +class IsAdminOrManager(BaseRolePermission): + message = "คุณต้องมีสิทธิ์ Admin หรือ Manager." + + def has_permission(self, request, view): + # Admin หรือ Manager (is_staff) + return self.is_admin(request.user) or self.is_manager(request.user) + + +# ---------------------------------------------------- +# 3. Permission: อนุญาตเฉพาะผู้ใช้ที่ผ่านการยืนยันตัวตนแล้ว (IsAuthenticated) +# และเป็น Viewer ขึ้นไป +# ---------------------------------------------------- +# ไม่จำเป็นต้องสร้างคลาสนี้ถ้าใช้ DRF's IsAuthenticated โดยตรง +# แต่ถ้าต้องการเพิ่ม Logic อื่นๆ ในอนาคต ควรใช้คลาสนี้ +class IsAuthenticatedViewer(permissions.BasePermission): + message = "คุณต้องเข้าสู่ระบบก่อน" + + def has_permission(self, request, view): + return request.user.is_authenticated \ No newline at end of file