from rest_framework.permissions import BasePermission, SAFE_METHODS def can_publish_or_moderate_post(user, institution=None) -> bool: """ Evaluates dynamic permission capability without hardcoding any role names or slugs: 1. User must be active and authenticated. 2. Superuser / Super Admin has global authority. 3. User must hold the capability flag: user.has_perm_flag('can_publish_posts_directly'). 4. If an institution is associated with the post: - User must be affiliated with this institution (as a member in institution.members), OR - User's managed_regions covers the institution's geographical region. 5. Standalone post (no institution): - User holds 'can_publish_posts_directly'. """ if not user or not user.is_authenticated or not user.is_active: return False if user.is_superuser or getattr(user, 'is_super_admin', False): return True # Dynamic capability check (configurable in Django Admin on Role / custom_permissions) if not user.has_perm_flag('can_publish_posts_directly'): return False if institution: # Check direct institution membership affiliation if institution.members.filter(user=user).exists(): return True # Check managed_regions (if user has managed regions covering this institution's region) if hasattr(user, 'managed_regions') and getattr(institution, 'region_id', None): if user.managed_regions.filter(id=institution.region_id).exists(): return True return False return True class IsAuthorOrEditorOrReadOnly(BasePermission): """ Read access is public (handled at view queryset level for status). Object write/delete access: - Author can edit their own post (draft, pending_review, rejected). - Editors satisfying can_publish_or_moderate_post() can edit, approve, or reject. """ def has_permission(self, request, view): if request.method in SAFE_METHODS: return True return bool(request.user and request.user.is_authenticated and request.user.is_active) def has_object_permission(self, request, view, obj): if request.method in SAFE_METHODS: return True if not request.user or not request.user.is_authenticated or not request.user.is_active: return False if request.user.is_superuser or getattr(request.user, 'is_super_admin', False): return True # Editor / Moderator with capability over post institution if can_publish_or_moderate_post(request.user, getattr(obj, 'institution', None)): return True # Post author or owning institution member can edit or delete their own post depending on HTTP method is_owner = False if hasattr(obj, 'author') and obj.author == request.user: is_owner = True elif getattr(obj, 'institution', None) and hasattr(obj.institution, 'members'): if obj.institution.members.filter(user=request.user, role__in=['admin', 'editor']).exists(): is_owner = True if is_owner: if request.method == 'DELETE': return request.user.has_perm_flag('can_delete_own_posts') return request.user.has_perm_flag('can_edit_own_posts') return False class CanPublishPost(BasePermission): """ Validates that authenticated user has capability to create posts (can_create_posts). Actual status transitions (draft vs pending_review vs published) are enforced by serializer. """ def has_permission(self, request, view): if request.method in SAFE_METHODS: return True if not request.user or not request.user.is_authenticated or not request.user.is_active: return False if request.user.is_superuser or getattr(request.user, 'is_super_admin', False): return True return request.user.has_perm_flag('can_create_posts')