@@ -59,6 +59,11 @@ class BaseScopePermission(BasePermission, metaclass=PermissionMeta):
5959 def get_scope_value (self , request ) -> str | None :
6060 """Extract the scope value from the request.
6161
62+ When a ``scopes`` list is provided, returns only the first element.
63+ This is intentional: bulk requests are expected to be homogeneous
64+ (all scopes must share the same namespace). Actual per-scope permission
65+ validation for bulk requests is handled in ``DynamicScopePermission``.
66+
6267 Args:
6368 request: The Django REST framework request object.
6469
@@ -92,6 +97,12 @@ def get_scope_namespace(self, request) -> str:
9297 >>> permission.get_scope_namespace(request)
9398 'global'
9499 """
100+ scopes_list = request .data .get ("scopes" )
101+ if scopes_list and isinstance (scopes_list , list ):
102+ if not self ._scopes_have_homogeneous_namespaces (scopes_list ):
103+ raise ValueError (
104+ f"Mixed scope namespaces in bulk request are not allowed: { scopes_list } "
105+ )
95106 scope_value = self .get_scope_value (request )
96107 if not scope_value :
97108 return self .NAMESPACE
@@ -100,6 +111,22 @@ def get_scope_namespace(self, request) -> str:
100111 except ValueError :
101112 return self .NAMESPACE
102113
114+ def _scopes_have_homogeneous_namespaces (self , scopes_list : list [str ]) -> bool :
115+ """Check that all scopes in the list share the same namespace.
116+
117+ Args:
118+ scopes_list: List of scope values to check.
119+ Returns:
120+ bool: True if all scopes share the same namespace, False otherwise.
121+ """
122+ namespaces = set ()
123+ for scope in scopes_list :
124+ try :
125+ namespaces .add (api .ScopeData (external_key = scope ).NAMESPACE )
126+ except ValueError :
127+ pass
128+ return len (namespaces ) <= 1
129+
103130 def has_permission (self , request , view ) -> bool :
104131 """Fallback permission check (deny by default).
105132
@@ -146,6 +173,9 @@ class DynamicScopePermission(BaseScopePermission):
146173
147174 Note:
148175 Superusers and staff members always have permission regardless of scope.
176+ Bulk requests (``scopes`` list) must be homogeneous — all scopes must share
177+ the same namespace (e.g., all ``course-v1:`` or all ``lib:``). Mixed namespaces
178+ will raise a ``ValueError`` during namespace resolution.
149179 """
150180
151181 NAMESPACE : ClassVar [None ] = None
0 commit comments