From 9aac93bdb537a7b2dbf95a15b2c20c6568195b6a Mon Sep 17 00:00:00 2001 From: Sanghun Lee Date: Thu, 12 Mar 2026 17:51:35 +0900 Subject: [PATCH 1/3] refactor(BA-5046): migrate artifact registry action classes to RBAC base classes Co-Authored-By: Claude Opus 4.6 --- .../artifact_registry/actions/base.py | 3 -- .../actions/common/get_meta.py | 28 +++++++++++++------ .../services/artifact_registry/processors.py | 9 ++++-- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/ai/backend/manager/services/artifact_registry/actions/base.py b/src/ai/backend/manager/services/artifact_registry/actions/base.py index 6135a54f77d..fbe9b450946 100644 --- a/src/ai/backend/manager/services/artifact_registry/actions/base.py +++ b/src/ai/backend/manager/services/artifact_registry/actions/base.py @@ -1,4 +1,3 @@ -from dataclasses import dataclass from typing import override from ai.backend.common.data.permission.types import EntityType @@ -11,7 +10,6 @@ from ai.backend.manager.actions.action.types import FieldData -@dataclass class ArtifactRegistryAction(BaseAction): @override @classmethod @@ -19,7 +17,6 @@ def entity_type(cls) -> EntityType: return EntityType.ARTIFACT_REGISTRY -@dataclass class ArtifactBatchRegistryAction(BaseBatchAction): @override @classmethod diff --git a/src/ai/backend/manager/services/artifact_registry/actions/common/get_meta.py b/src/ai/backend/manager/services/artifact_registry/actions/common/get_meta.py index 196ee0dc5e4..54bd140684c 100644 --- a/src/ai/backend/manager/services/artifact_registry/actions/common/get_meta.py +++ b/src/ai/backend/manager/services/artifact_registry/actions/common/get_meta.py @@ -2,31 +2,41 @@ from dataclasses import dataclass from typing import override -from ai.backend.manager.actions.action import BaseActionResult +from ai.backend.common.data.permission.types import RBACElementType from ai.backend.manager.actions.types import ActionOperationType from ai.backend.manager.data.artifact_registries.types import ArtifactRegistryData -from ai.backend.manager.services.artifact_registry.actions.base import ArtifactRegistryAction +from ai.backend.manager.data.permission.types import RBACElementRef +from ai.backend.manager.services.artifact_registry.actions.base import ( + ArtifactRegistrySingleEntityAction, + ArtifactRegistrySingleEntityActionResult, +) @dataclass -class GetArtifactRegistryMetaAction(ArtifactRegistryAction): +class GetArtifactRegistryMetaAction(ArtifactRegistrySingleEntityAction): registry_id: uuid.UUID | None = None registry_name: str | None = None - @override - def entity_id(self) -> str | None: - return str(self.registry_id) - @override @classmethod def operation_type(cls) -> ActionOperationType: return ActionOperationType.GET + @override + def target_entity_id(self) -> str: + if self.registry_id: + return str(self.registry_id) + return self.registry_name or "" + + @override + def target_element(self) -> RBACElementRef: + return RBACElementRef(RBACElementType.ARTIFACT_REGISTRY, self.target_entity_id()) + @dataclass -class GetArtifactRegistryMetaActionResult(BaseActionResult): +class GetArtifactRegistryMetaActionResult(ArtifactRegistrySingleEntityActionResult): result: ArtifactRegistryData @override - def entity_id(self) -> str | None: + def target_entity_id(self) -> str: return str(self.result.id) diff --git a/src/ai/backend/manager/services/artifact_registry/processors.py b/src/ai/backend/manager/services/artifact_registry/processors.py index 47a4463dec9..9c1bb1f3ff3 100644 --- a/src/ai/backend/manager/services/artifact_registry/processors.py +++ b/src/ai/backend/manager/services/artifact_registry/processors.py @@ -121,7 +121,7 @@ class ArtifactRegistryProcessors(AbstractProcessorPackage): search_reservoir_registries: ActionProcessor[ SearchReservoirRegistriesAction, SearchReservoirRegistriesActionResult ] - get_registry_meta: ActionProcessor[ + get_registry_meta: SingleEntityActionProcessor[ GetArtifactRegistryMetaAction, GetArtifactRegistryMetaActionResult ] get_registry_metas: ActionProcessor[ @@ -192,6 +192,12 @@ def __init__( validators=[validators.rbac.single_entity], ) + self.get_registry_meta = SingleEntityActionProcessor( + service.get_registry_meta, + action_monitors, + validators=[validators.rbac.single_entity], + ) + # Internal/batch actions without RBAC self.get_huggingface_registries = ActionProcessor( service.get_huggingface_registries, action_monitors @@ -199,7 +205,6 @@ def __init__( self.get_reservoir_registries = ActionProcessor( service.get_reservoir_registries, action_monitors ) - self.get_registry_meta = ActionProcessor(service.get_registry_meta, action_monitors) self.get_registry_metas = ActionProcessor(service.get_registry_metas, action_monitors) @override From 31a745ff548174c4f30a87f4e71a42493527d5d4 Mon Sep 17 00:00:00 2001 From: Sanghun Lee Date: Thu, 12 Mar 2026 18:21:25 +0900 Subject: [PATCH 2/3] changelog: add news fragment for PR #9992 --- changes/9992.enhance.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/9992.enhance.md diff --git a/changes/9992.enhance.md b/changes/9992.enhance.md new file mode 100644 index 00000000000..bf068744ca7 --- /dev/null +++ b/changes/9992.enhance.md @@ -0,0 +1 @@ +Refactor artifact registry action classes to RBAC-aware base classes (`ArtifactRegistrySingleEntityAction`) to enable RBAC validator wiring From 3e0b7c2d568ea9a9132ea330d4d5eae01e1017e0 Mon Sep 17 00:00:00 2001 From: Sanghun Lee Date: Fri, 20 Mar 2026 13:28:56 +0900 Subject: [PATCH 3/3] fix(BA-5046): raise InvalidAPIParameters when both registry_id and registry_name are null Co-Authored-By: Claude Opus 4.6 --- .../services/artifact_registry/actions/common/get_meta.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ai/backend/manager/services/artifact_registry/actions/common/get_meta.py b/src/ai/backend/manager/services/artifact_registry/actions/common/get_meta.py index 54bd140684c..9585d13e570 100644 --- a/src/ai/backend/manager/services/artifact_registry/actions/common/get_meta.py +++ b/src/ai/backend/manager/services/artifact_registry/actions/common/get_meta.py @@ -6,6 +6,7 @@ from ai.backend.manager.actions.types import ActionOperationType from ai.backend.manager.data.artifact_registries.types import ArtifactRegistryData from ai.backend.manager.data.permission.types import RBACElementRef +from ai.backend.manager.errors.api import InvalidAPIParameters from ai.backend.manager.services.artifact_registry.actions.base import ( ArtifactRegistrySingleEntityAction, ArtifactRegistrySingleEntityActionResult, @@ -26,7 +27,9 @@ def operation_type(cls) -> ActionOperationType: def target_entity_id(self) -> str: if self.registry_id: return str(self.registry_id) - return self.registry_name or "" + if self.registry_name: + return self.registry_name + raise InvalidAPIParameters("Either registry_id or registry_name must be provided.") @override def target_element(self) -> RBACElementRef: