@@ -203,16 +203,46 @@ def _get_permission_instance(self, request) -> BaseScopePermission:
203203 perm_class = PermissionMeta .get_permission_class (scope_namespace )
204204 return perm_class ()
205205
206+ def _has_bulk_permission (self , request , view , scopes_list : list [str ]) -> bool :
207+ """Check permissions for a bulk request carrying multiple scopes.
208+
209+ Bulk operations are only supported for endpoints decorated with
210+ ``@authz_permissions``. A handler that does not use the decorator (i.e. does
211+ not mix in ``MethodPermissionMixin``) has no declared permissions to evaluate
212+ per-scope, so bulk access is denied outright.
213+
214+ Every scope in ``scopes_list`` must pass at least one of the required
215+ permissions declared by the decorator (OR logic per permission, AND logic
216+ across scopes).
217+
218+ Args:
219+ request: The Django REST framework request object.
220+ view: The view being accessed.
221+ scopes_list: The list of scope values from ``request.data["scopes"]``.
222+
223+ Returns:
224+ bool: True only if every scope passes at least one required permission.
225+ """
226+ perm_instance = self ._get_permission_instance (request ) # namespace resolved from scopes[0]
227+ # Bulk without @authz_permissions decorator is not supported: there are no
228+ # per-method permissions to iterate over, so we cannot safely grant access.
229+ if not isinstance (perm_instance , MethodPermissionMixin ):
230+ return False
231+ required = perm_instance .get_required_permissions (request , view )
232+ if not required :
233+ return False
234+ return all (perm_instance .validate_permissions (request , required , sv ) for sv in scopes_list )
235+
206236 def has_permission (self , request , view ) -> bool :
207237 """Delegate permission check to the appropriate scope-specific permission class.
208238
209239 Superusers and staff members are automatically granted permission. For other
210240 users, the permission check is delegated to the permission class registered
211241 for the request's scope namespace.
212242
213- For bulk PUT requests that carry a ``scopes`` list, every scope in the list
214- must pass at least one of the required permissions (OR logic per permission,
215- AND logic across scopes).
243+ For bulk requests that carry a ``scopes`` list, delegates to
244+ ``_has_bulk_permission``: every scope must pass at least one of the required
245+ permissions (OR logic per permission, AND logic across scopes).
216246
217247 Examples:
218248 >>> # Regular user gets scope-specific check
@@ -223,13 +253,7 @@ def has_permission(self, request, view) -> bool:
223253 return True
224254 scopes_list = request .data .get ("scopes" )
225255 if scopes_list and isinstance (scopes_list , list ):
226- perm_instance = self ._get_permission_instance (request ) # namespace resolved from scopes[0]
227- if not isinstance (perm_instance , MethodPermissionMixin ):
228- return False
229- required = perm_instance .get_required_permissions (request , view )
230- if not required :
231- return False
232- return all (perm_instance .validate_permissions (request , required , sv ) for sv in scopes_list )
256+ return self ._has_bulk_permission (request , view , scopes_list )
233257 return self ._get_permission_instance (request ).has_permission (request , view )
234258
235259 def has_object_permission (self , request , view , obj ) -> bool :
0 commit comments