Skip to content

Commit 966cf2e

Browse files
fregataaclaude
andcommitted
fix(BA-4525): Raise error instead of branching on GLOBAL scope in search_scopes
Remove GLOBAL scope early-return from handler (scope_type.to_element() now raises RBACTypeConversionError naturally), remove GLOBAL branch from ScopeAdapter, remove get_global_scope() from repository, and delete the corresponding test. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bd2ac5b commit 966cf2e

4 files changed

Lines changed: 3 additions & 59 deletions

File tree

src/ai/backend/manager/api/rest/rbac/handler.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
from ai.backend.common.api_handlers import APIResponse, BodyParam, PathParam
1414
from ai.backend.common.data.permission.types import (
15-
GLOBAL_SCOPE_ID,
1615
EntityType,
1716
ScopeType,
1817
)
@@ -50,9 +49,7 @@
5049
SearchScopesResponse,
5150
)
5251
from ai.backend.common.exception import RBACTypeConversionError
53-
from ai.backend.manager.data.permission.id import ScopeId
5452
from ai.backend.manager.data.permission.role import UserRoleAssignmentInput, UserRoleRevocationInput
55-
from ai.backend.manager.data.permission.types import ScopeData
5653
from ai.backend.manager.dto.context import UserContext
5754
from ai.backend.manager.errors.permission import NotEnoughPermission
5855
from ai.backend.manager.models.rbac_models.role import RoleRow
@@ -326,21 +323,6 @@ async def search_scopes(
326323
raise NotEnoughPermission("Only superadmin can search scopes.")
327324

328325
scope_type = path.parsed.scope_type
329-
# Handle GLOBAL scope as a static early-return (GLOBAL is not in RBACElementType)
330-
if scope_type.value == GLOBAL_SCOPE_ID:
331-
global_result = SearchScopesResponse(
332-
items=[
333-
self._scope_adapter.convert_to_dto(
334-
ScopeData(
335-
id=ScopeId(scope_type=ScopeType.GLOBAL, scope_id=GLOBAL_SCOPE_ID),
336-
name=GLOBAL_SCOPE_ID,
337-
)
338-
)
339-
],
340-
pagination=PaginationInfo(total=1, offset=0, limit=1),
341-
)
342-
return APIResponse.build(status_code=HTTPStatus.OK, response_model=global_result)
343-
344326
element_type = scope_type.to_element()
345327
querier = self._scope_adapter.build_querier(scope_type, body.parsed)
346328
action = SearchScopesAction(element_type=element_type, querier=querier)

src/ai/backend/manager/api/rest/rbac/scope_adapter.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ class ScopeAdapter(BaseFilterAdapter):
4141
def build_querier(self, scope_type: ScopeType, request: SearchScopesRequest) -> BatchQuerier:
4242
"""Build a BatchQuerier based on scope type."""
4343
match scope_type:
44-
case ScopeType.GLOBAL:
45-
return self._build_global_scope_querier(request)
4644
case ScopeType.DOMAIN:
4745
return self._build_domain_scope_querier(request)
4846
case ScopeType.PROJECT:
@@ -78,11 +76,6 @@ def _build_user_scope_querier(self, request: SearchScopesRequest) -> BatchQuerie
7876

7977
return BatchQuerier(conditions=conditions, orders=orders, pagination=pagination)
8078

81-
def _build_global_scope_querier(self, request: SearchScopesRequest) -> BatchQuerier:
82-
"""Build a BatchQuerier for global scope (no filtering needed)."""
83-
pagination = OffsetPagination(limit=request.limit, offset=request.offset)
84-
return BatchQuerier(conditions=[], orders=[], pagination=pagination)
85-
8679
def _convert_domain_filter(self, filter: ScopeFilter) -> list[QueryCondition]:
8780
"""Convert scope filter to domain query conditions."""
8881
conditions: list[QueryCondition] = []

src/ai/backend/manager/repositories/permission_controller/repository.py

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
from collections.abc import Mapping
55
from typing import cast
66

7-
from ai.backend.common.data.permission.types import GLOBAL_SCOPE_ID, OperationType, RBACElementType
7+
from ai.backend.common.data.permission.types import OperationType, RBACElementType
88
from ai.backend.common.exception import BackendAIError
99
from ai.backend.common.metrics.metric import DomainType, LayerType
1010
from ai.backend.common.resilience.policies.metrics import MetricArgs, MetricPolicy
1111
from ai.backend.common.resilience.policies.retry import BackoffStrategy, RetryArgs, RetryPolicy
1212
from ai.backend.common.resilience.resilience import Resilience
1313
from ai.backend.manager.data.permission.entity import ElementAssociationListResult, EntityListResult
14-
from ai.backend.manager.data.permission.id import ObjectId, ScopeId
14+
from ai.backend.manager.data.permission.id import ObjectId
1515
from ai.backend.manager.data.permission.object_permission import (
1616
ObjectPermissionData,
1717
ObjectPermissionListResult,
@@ -40,9 +40,7 @@
4040
)
4141
from ai.backend.manager.data.permission.types import (
4242
RBACElementRef,
43-
ScopeData,
4443
ScopeListResult,
45-
ScopeType,
4644
)
4745
from ai.backend.manager.models.rbac_models.permission.object_permission import ObjectPermissionRow
4846
from ai.backend.manager.models.rbac_models.permission.permission import PermissionRow
@@ -303,20 +301,6 @@ async def search_users_assigned_to_role(
303301
querier=querier,
304302
)
305303

306-
def get_global_scope(self) -> ScopeListResult:
307-
"""Get the global scope as a static result."""
308-
return ScopeListResult(
309-
items=[
310-
ScopeData(
311-
id=ScopeId(scope_type=ScopeType.GLOBAL, scope_id=GLOBAL_SCOPE_ID),
312-
name=GLOBAL_SCOPE_ID,
313-
)
314-
],
315-
total_count=1,
316-
has_next_page=False,
317-
has_previous_page=False,
318-
)
319-
320304
@permission_controller_repository_resilience.apply()
321305
async def search_scopes(
322306
self,

tests/unit/manager/repositories/permission_controller/test_search_scopes.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import pytest
1212

1313
from ai.backend.common.data.filter_specs import StringMatchSpec
14-
from ai.backend.common.data.permission.types import GLOBAL_SCOPE_ID, RBACElementType, ScopeType
14+
from ai.backend.common.data.permission.types import RBACElementType, ScopeType
1515
from ai.backend.common.types import ResourceSlot
1616
from ai.backend.manager.models.agent import AgentRow
1717
from ai.backend.manager.models.deployment_auto_scaling_policy import DeploymentAutoScalingPolicyRow
@@ -781,21 +781,6 @@ def permission_controller_repository(
781781
"""Create PermissionControllerRepository instance."""
782782
return PermissionControllerRepository(db_with_scope_tables)
783783

784-
async def test_search_scopes_global_returns_static_result(
785-
self,
786-
permission_controller_repository: PermissionControllerRepository,
787-
) -> None:
788-
"""Test global scope returns single static result."""
789-
result = permission_controller_repository.get_global_scope()
790-
791-
assert result.total_count == 1
792-
assert len(result.items) == 1
793-
assert result.items[0].id.scope_type == ScopeType.GLOBAL
794-
assert result.items[0].id.scope_id == GLOBAL_SCOPE_ID
795-
assert result.items[0].name == GLOBAL_SCOPE_ID
796-
assert result.has_next_page is False
797-
assert result.has_previous_page is False
798-
799784

800785
class TestSearchScopesEmptyResult:
801786
"""Tests for empty result handling."""

0 commit comments

Comments
 (0)