Implement security enhancements and unified response formats across API endpoints. Update permission classes for various ViewSets to ensure proper tenant isolation and compliance with API standards. Refactor authentication endpoints to utilize success and error response helpers, improving error tracking and response consistency. Complete documentation updates reflecting these changes and achieving full compliance with API Standard v1.0.

This commit is contained in:
Desktop
2025-11-16 11:35:47 +05:00
parent d492b74d40
commit 64b8280bce
8 changed files with 739 additions and 93 deletions

View File

@@ -11,7 +11,7 @@ from drf_spectacular.utils import extend_schema, extend_schema_view
from igny8_core.api.base import AccountModelViewSet
from igny8_core.api.response import success_response, error_response
from igny8_core.api.throttles import DebugScopedRateThrottle
from igny8_core.api.permissions import IsAuthenticatedAndActive, IsAdminOrOwner
from igny8_core.api.permissions import IsAuthenticatedAndActive, HasTenantAccess, IsAdminOrOwner
from django.conf import settings
logger = logging.getLogger(__name__)
@@ -31,7 +31,7 @@ class IntegrationSettingsViewSet(viewsets.ViewSet):
Following reference plugin pattern: WordPress uses update_option() for igny8_api_settings
We store in IntegrationSettings model with account isolation
"""
permission_classes = [IsAuthenticatedAndActive, IsAdminOrOwner]
permission_classes = [IsAuthenticatedAndActive, HasTenantAccess, IsAdminOrOwner]
throttle_scope = 'system_admin'
throttle_classes = [DebugScopedRateThrottle]

View File

@@ -12,6 +12,7 @@ from igny8_core.api.response import success_response, error_response
from igny8_core.api.authentication import JWTAuthentication, CSRFExemptSessionAuthentication
from igny8_core.api.pagination import CustomPageNumberPagination
from igny8_core.api.throttles import DebugScopedRateThrottle
from igny8_core.api.permissions import IsAuthenticatedAndActive, HasTenantAccess, IsAdminOrOwner
from .settings_models import SystemSettings, AccountSettings, UserSettings, ModuleSettings, AISettings
from .settings_serializers import (
SystemSettingsSerializer, AccountSettingsSerializer, UserSettingsSerializer,
@@ -34,7 +35,7 @@ class SystemSettingsViewSet(AccountModelViewSet):
"""
queryset = SystemSettings.objects.all()
serializer_class = SystemSettingsSerializer
permission_classes = [permissions.IsAuthenticated] # Require authentication
permission_classes = [IsAuthenticatedAndActive, HasTenantAccess]
authentication_classes = [JWTAuthentication, CSRFExemptSessionAuthentication]
pagination_class = CustomPageNumberPagination
throttle_scope = 'system'
@@ -43,8 +44,8 @@ class SystemSettingsViewSet(AccountModelViewSet):
def get_permissions(self):
"""Admin only for write operations, read for authenticated users"""
if self.action in ['create', 'update', 'partial_update', 'destroy']:
return [permissions.IsAdminUser()]
return [permissions.IsAuthenticated()]
return [IsAdminOrOwner()]
return [IsAuthenticatedAndActive(), HasTenantAccess()]
def get_queryset(self):
"""Get all system settings"""
@@ -85,7 +86,7 @@ class AccountSettingsViewSet(AccountModelViewSet):
"""
queryset = AccountSettings.objects.all()
serializer_class = AccountSettingsSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [IsAuthenticatedAndActive, HasTenantAccess]
authentication_classes = [JWTAuthentication, CSRFExemptSessionAuthentication]
pagination_class = CustomPageNumberPagination
throttle_scope = 'system'
@@ -145,7 +146,7 @@ class UserSettingsViewSet(AccountModelViewSet):
"""
queryset = UserSettings.objects.all()
serializer_class = UserSettingsSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [IsAuthenticatedAndActive, HasTenantAccess]
authentication_classes = [JWTAuthentication, CSRFExemptSessionAuthentication]
pagination_class = CustomPageNumberPagination
throttle_scope = 'system'
@@ -211,7 +212,7 @@ class ModuleSettingsViewSet(AccountModelViewSet):
"""
queryset = ModuleSettings.objects.all()
serializer_class = ModuleSettingsSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [IsAuthenticatedAndActive, HasTenantAccess]
authentication_classes = [JWTAuthentication, CSRFExemptSessionAuthentication]
pagination_class = CustomPageNumberPagination
throttle_scope = 'system'
@@ -290,7 +291,7 @@ class AISettingsViewSet(AccountModelViewSet):
"""
queryset = AISettings.objects.all()
serializer_class = AISettingsSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [IsAuthenticatedAndActive, HasTenantAccess]
authentication_classes = [JWTAuthentication, CSRFExemptSessionAuthentication]
pagination_class = CustomPageNumberPagination
throttle_scope = 'system'

View File

@@ -15,7 +15,7 @@ from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema, extend_schema_view
from igny8_core.api.base import AccountModelViewSet
from igny8_core.api.response import success_response, error_response
from igny8_core.api.permissions import IsEditorOrAbove, IsAuthenticatedAndActive, IsViewerOrAbove
from igny8_core.api.permissions import IsEditorOrAbove, IsAuthenticatedAndActive, IsViewerOrAbove, HasTenantAccess
from igny8_core.api.throttles import DebugScopedRateThrottle
from igny8_core.api.pagination import CustomPageNumberPagination
from .models import AIPrompt, AuthorProfile, Strategy
@@ -39,7 +39,7 @@ class AIPromptViewSet(AccountModelViewSet):
"""
queryset = AIPrompt.objects.all()
serializer_class = AIPromptSerializer
permission_classes = [] # Allow any for now (backward compatibility)
permission_classes = [IsAuthenticatedAndActive, HasTenantAccess]
throttle_scope = 'system'
throttle_classes = [DebugScopedRateThrottle]
pagination_class = CustomPageNumberPagination # Explicitly use custom pagination
@@ -72,6 +72,14 @@ class AIPromptViewSet(AccountModelViewSet):
@action(detail=False, methods=['post'], url_path='save', url_name='save')
def save_prompt(self, request):
"""Save or update a prompt - requires editor or above"""
# Check if user has editor or above permissions
if not IsEditorOrAbove().has_permission(request, self):
return error_response(
error='Permission denied. Editor or above role required.',
status_code=http_status.HTTP_403_FORBIDDEN,
request=request
)
prompt_type = request.data.get('prompt_type')
prompt_value = request.data.get('prompt_value')
@@ -140,7 +148,15 @@ class AIPromptViewSet(AccountModelViewSet):
@action(detail=False, methods=['post'], url_path='reset', url_name='reset')
def reset_prompt(self, request):
"""Reset prompt to default"""
"""Reset prompt to default - requires editor or above"""
# Check if user has editor or above permissions
if not IsEditorOrAbove().has_permission(request, self):
return error_response(
error='Permission denied. Editor or above role required.',
status_code=http_status.HTTP_403_FORBIDDEN,
request=request
)
prompt_type = request.data.get('prompt_type')
if not prompt_type: