Skip to content

Commit 618a231

Browse files
fregataaclaude
andcommitted
refactor(BA-5040): Remove RBAC validator from SearchImagesAction
Revert SearchImagesAction to plain ImageAction without scope metadata. Remove user_uuid/domain_name fields, scope methods, and switch processor back to ActionProcessor. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent af8705d commit 618a231

8 files changed

Lines changed: 22 additions & 48 deletions

File tree

src/ai/backend/manager/api/gql/data_loader/image/loader.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from collections.abc import Sequence
44

5-
from ai.backend.common.contexts.user import current_user
65
from ai.backend.common.types import ImageID
76
from ai.backend.manager.data.image.types import ImageData
87
from ai.backend.manager.repositories.base import BatchQuerier, NoPagination
@@ -32,9 +31,8 @@ async def load_images_by_ids(
3231
conditions=[ImageConditions.by_ids(image_ids)],
3332
)
3433

35-
user = current_user()
3634
action_result = await processor.search_images.wait_for_complete(
37-
SearchImagesAction(querier=querier, user_uuid=str(user.user_id) if user else "")
35+
SearchImagesAction(querier=querier)
3836
)
3937

4038
image_map: dict[ImageID, ImageData] = {ImageID(image.id): image for image in action_result.data}

src/ai/backend/manager/api/gql/image/fetcher.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import strawberry
1313
from strawberry import Info
1414

15-
from ai.backend.common.contexts.user import current_user
1615
from ai.backend.common.types import ImageID
1716
from ai.backend.manager.api.gql.adapter import PaginationOptions, PaginationSpec
1817
from ai.backend.manager.api.gql.base import encode_cursor
@@ -85,9 +84,8 @@ async def fetch_images(
8584
base_conditions=base_conditions,
8685
)
8786

88-
user = current_user()
8987
action_result = await info.context.processors.image.search_images.wait_for_complete(
90-
SearchImagesAction(querier=querier, user_uuid=str(user.user_id) if user else "")
88+
SearchImagesAction(querier=querier)
9189
)
9290

9391
edges = []

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,7 @@ async def search(
5656
log.info("SEARCH (ak:{})", ctx.access_key)
5757
querier = self._adapter.build_querier(body.parsed)
5858
action_result = await self._image.search_images.wait_for_complete(
59-
SearchImagesAction(
60-
querier=querier,
61-
user_uuid=str(ctx.user_uuid),
62-
)
59+
SearchImagesAction(querier=querier)
6360
)
6461
resp = SearchImagesResponse(
6562
items=[self._adapter.convert_to_dto(img) for img in action_result.data],

src/ai/backend/manager/services/image/actions/search_images.py

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,34 @@
33
from dataclasses import dataclass
44
from typing import override
55

6-
from ai.backend.common.data.permission.types import RBACElementType, ScopeType
6+
from ai.backend.manager.actions.action import BaseActionResult
77
from ai.backend.manager.actions.types import ActionOperationType
88
from ai.backend.manager.data.image.types import ImageData
9-
from ai.backend.manager.data.permission.types import RBACElementRef
109
from ai.backend.manager.repositories.base import BatchQuerier
11-
from ai.backend.manager.services.image.actions.base import ImageScopeAction, ImageScopeActionResult
10+
from ai.backend.manager.services.image.actions.base import ImageAction
1211

1312

1413
@dataclass
15-
class SearchImagesAction(ImageScopeAction):
14+
class SearchImagesAction(ImageAction):
1615
querier: BatchQuerier
17-
user_uuid: str
16+
17+
@override
18+
def entity_id(self) -> str | None:
19+
return None
1820

1921
@override
2022
@classmethod
2123
def operation_type(cls) -> ActionOperationType:
2224
return ActionOperationType.SEARCH
2325

24-
@override
25-
def scope_type(self) -> ScopeType:
26-
# Images are scoped to the user
27-
return ScopeType.USER
28-
29-
@override
30-
def scope_id(self) -> str:
31-
return self.user_uuid
32-
33-
@override
34-
def target_element(self) -> RBACElementRef:
35-
return RBACElementRef(RBACElementType.USER, self.user_uuid)
36-
3726

3827
@dataclass
39-
class SearchImagesActionResult(ImageScopeActionResult):
28+
class SearchImagesActionResult(BaseActionResult):
4029
data: list[ImageData]
4130
total_count: int
4231
has_next_page: bool
4332
has_previous_page: bool
44-
user_uuid: str = ""
45-
46-
@override
47-
def scope_type(self) -> ScopeType:
48-
return ScopeType.USER
4933

5034
@override
51-
def scope_id(self) -> str:
52-
return self.user_uuid
35+
def entity_id(self) -> str | None:
36+
return None

src/ai/backend/manager/services/image/processors.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ class ImageProcessors(AbstractProcessorPackage):
132132
GetImageInstalledAgentsAction, GetImageInstalledAgentsActionResult
133133
]
134134
get_all_images: ScopeActionProcessor[GetAllImagesAction, GetAllImagesActionResult]
135-
search_images: ScopeActionProcessor[SearchImagesAction, SearchImagesActionResult]
135+
search_images: ActionProcessor[SearchImagesAction, SearchImagesActionResult]
136136
search_aliases: ActionProcessor[SearchAliasesAction, SearchAliasesActionResult]
137137
load_image_last_used: ActionProcessor[LoadImageLastUsedAction, LoadImageLastUsedActionResult]
138138

@@ -159,9 +159,7 @@ def __init__(
159159
self.get_all_images = ScopeActionProcessor(
160160
service.get_all_images, action_monitors, validators=[validators.rbac.scope]
161161
)
162-
self.search_images = ScopeActionProcessor(
163-
service.search_images, action_monitors, validators=[validators.rbac.scope]
164-
)
162+
self.search_images = ActionProcessor(service.search_images, action_monitors)
165163

166164
# Single entity actions with RBAC validation
167165
self.forget_image_by_id = SingleEntityActionProcessor(

src/ai/backend/manager/services/image/service.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,6 @@ async def search_images(self, action: SearchImagesAction) -> SearchImagesActionR
402402
total_count=result.total_count,
403403
has_next_page=result.has_next_page,
404404
has_previous_page=result.has_previous_page,
405-
user_uuid=action.user_uuid,
406405
)
407406

408407
async def alias_image_by_id(self, action: AliasImageByIdAction) -> AliasImageByIdActionResult:

tests/unit/manager/api/image/test_handler.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ async def test_search_images_calls_processor(
428428
) -> None:
429429
"""Search handler should call search_images processor."""
430430
await mock_processors.image.search_images.wait_for_complete(
431-
SearchImagesAction(querier=MagicMock(), user_uuid="test-user")
431+
SearchImagesAction(querier=MagicMock())
432432
)
433433
mock_processors.image.search_images.wait_for_complete.assert_called_once()
434434

@@ -438,7 +438,7 @@ async def test_search_images_returns_correct_count(
438438
) -> None:
439439
"""Search result should have correct total count."""
440440
result = await mock_processors.image.search_images.wait_for_complete(
441-
SearchImagesAction(querier=MagicMock(), user_uuid="test-user")
441+
SearchImagesAction(querier=MagicMock())
442442
)
443443
assert result.total_count == 2
444444
assert len(result.data) == 2
@@ -449,7 +449,7 @@ async def test_search_images_converts_to_dto(
449449
) -> None:
450450
"""Search result data should be convertible to DTOs."""
451451
result = await mock_processors.image.search_images.wait_for_complete(
452-
SearchImagesAction(querier=MagicMock(), user_uuid="test-user")
452+
SearchImagesAction(querier=MagicMock())
453453
)
454454
adapter = ImageAdapter()
455455
items = [adapter.convert_to_dto(img) for img in result.data]
@@ -591,7 +591,7 @@ async def test_search_empty_result(self) -> None:
591591
processors.image.search_images.wait_for_complete = AsyncMock(return_value=result)
592592

593593
search_result = await processors.image.search_images.wait_for_complete(
594-
SearchImagesAction(querier=MagicMock(), user_uuid="test-user")
594+
SearchImagesAction(querier=MagicMock())
595595
)
596596
assert search_result.data == []
597597
assert search_result.total_count == 0

tests/unit/manager/services/image/test_image_service.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,7 @@ async def test_search_images_success(
903903
conditions=[],
904904
orders=[],
905905
)
906-
action = SearchImagesAction(querier=querier, user_uuid="test-user")
906+
action = SearchImagesAction(querier=querier)
907907

908908
result = await processors.search_images.wait_for_complete(action)
909909

@@ -933,7 +933,7 @@ async def test_search_images_empty_result(
933933
conditions=[],
934934
orders=[],
935935
)
936-
action = SearchImagesAction(querier=querier, user_uuid="test-user")
936+
action = SearchImagesAction(querier=querier)
937937

938938
result = await processors.search_images.wait_for_complete(action)
939939

@@ -961,7 +961,7 @@ async def test_search_images_with_pagination(
961961
conditions=[],
962962
orders=[],
963963
)
964-
action = SearchImagesAction(querier=querier, user_uuid="test-user")
964+
action = SearchImagesAction(querier=querier)
965965

966966
result = await processors.search_images.wait_for_complete(action)
967967

0 commit comments

Comments
 (0)