Skip to content

Commit 4b3d02f

Browse files
fregataaclaude
andauthored
feat(BA-3692): Apply RBAC validator for Model Deployment actions (#10033)
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent a22a6bd commit 4b3d02f

28 files changed

Lines changed: 298 additions & 117 deletions

changes/10033.feature.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Apply RBAC permission validators to model deployment service actions

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -469,9 +469,9 @@ async def update_route(
469469
traffic_ratio=params.traffic_ratio,
470470
)
471471

472-
result = await self._model_serving.update_route.wait_for_complete(action)
472+
await self._model_serving.update_route.wait_for_complete(action)
473473

474-
resp = SuccessResponseModel(success=result.success)
474+
resp = SuccessResponseModel(success=True)
475475
return APIResponse.build(HTTPStatus.OK, resp)
476476

477477
# ------------------------------------------------------------------
@@ -494,9 +494,9 @@ async def delete_route(
494494
route_id=path_params.route_id,
495495
)
496496

497-
result = await self._model_serving.delete_route.wait_for_complete(action)
497+
await self._model_serving.delete_route.wait_for_complete(action)
498498

499-
resp = SuccessResponseModel(success=result.success)
499+
resp = SuccessResponseModel(success=True)
500500
return APIResponse.build(HTTPStatus.OK, resp)
501501

502502
# ------------------------------------------------------------------

src/ai/backend/manager/services/model_serving/actions/base.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,42 @@
22

33
from ai.backend.common.data.permission.types import EntityType
44
from ai.backend.manager.actions.action import BaseAction
5+
from ai.backend.manager.actions.action.scope import BaseScopeAction, BaseScopeActionResult
6+
from ai.backend.manager.actions.action.single_entity import (
7+
BaseSingleEntityAction,
8+
BaseSingleEntityActionResult,
9+
)
10+
from ai.backend.manager.actions.action.types import FieldData
511

612

713
class ModelServiceAction(BaseAction):
814
@override
915
@classmethod
1016
def entity_type(cls) -> EntityType:
11-
return EntityType.MODEL_SERVICE
17+
return EntityType.MODEL_DEPLOYMENT
18+
19+
20+
class ModelServiceScopeAction(BaseScopeAction):
21+
@override
22+
@classmethod
23+
def entity_type(cls) -> EntityType:
24+
return EntityType.MODEL_DEPLOYMENT
25+
26+
27+
class ModelServiceScopeActionResult(BaseScopeActionResult):
28+
pass
29+
30+
31+
class ModelServiceSingleEntityAction(BaseSingleEntityAction):
32+
@override
33+
@classmethod
34+
def entity_type(cls) -> EntityType:
35+
return EntityType.MODEL_DEPLOYMENT
36+
37+
@override
38+
def field_data(self) -> FieldData | None:
39+
return None
40+
41+
42+
class ModelServiceSingleEntityActionResult(BaseSingleEntityActionResult):
43+
pass

src/ai/backend/manager/services/model_serving/actions/create_model_service.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,50 @@
44
from dataclasses import dataclass
55
from typing import override
66

7-
from ai.backend.manager.actions.action import BaseActionResult
7+
from ai.backend.common.data.permission.types import RBACElementType, ScopeType
88
from ai.backend.manager.actions.types import ActionOperationType
99
from ai.backend.manager.data.model_serving.creator import ModelServiceCreator
1010
from ai.backend.manager.data.model_serving.types import ServiceInfo
11-
from ai.backend.manager.services.model_serving.actions.base import ModelServiceAction
11+
from ai.backend.manager.data.permission.types import RBACElementRef
12+
from ai.backend.manager.services.model_serving.actions.base import (
13+
ModelServiceScopeAction,
14+
ModelServiceScopeActionResult,
15+
)
1216

1317

1418
@dataclass
15-
class CreateModelServiceAction(ModelServiceAction):
19+
class CreateModelServiceAction(ModelServiceScopeAction):
1620
request_user_id: uuid.UUID
1721
creator: ModelServiceCreator
18-
19-
@override
20-
def entity_id(self) -> str | None:
21-
return None
22+
_project_id: uuid.UUID
2223

2324
@override
2425
@classmethod
2526
def operation_type(cls) -> ActionOperationType:
2627
return ActionOperationType.CREATE
2728

29+
@override
30+
def scope_type(self) -> ScopeType:
31+
return ScopeType.PROJECT
32+
33+
@override
34+
def scope_id(self) -> str:
35+
return str(self._project_id)
36+
37+
@override
38+
def target_element(self) -> RBACElementRef:
39+
return RBACElementRef(RBACElementType.PROJECT, str(self._project_id))
40+
2841

2942
@dataclass
30-
class CreateModelServiceActionResult(BaseActionResult):
43+
class CreateModelServiceActionResult(ModelServiceScopeActionResult):
3144
data: ServiceInfo
45+
_project_id: uuid.UUID
46+
47+
@override
48+
def scope_type(self) -> ScopeType:
49+
return ScopeType.PROJECT
3250

3351
@override
34-
def entity_id(self) -> str | None:
35-
return str(self.data.endpoint_id)
52+
def scope_id(self) -> str:
53+
return str(self._project_id)

src/ai/backend/manager/services/model_serving/actions/delete_model_service.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,37 @@
22
from dataclasses import dataclass
33
from typing import override
44

5-
from ai.backend.manager.actions.action import BaseActionResult
5+
from ai.backend.common.data.permission.types import RBACElementType
66
from ai.backend.manager.actions.types import ActionOperationType
7-
from ai.backend.manager.services.model_serving.actions.base import ModelServiceAction
7+
from ai.backend.manager.data.permission.types import RBACElementRef
8+
from ai.backend.manager.services.model_serving.actions.base import (
9+
ModelServiceSingleEntityAction,
10+
ModelServiceSingleEntityActionResult,
11+
)
812

913

1014
@dataclass
11-
class DeleteModelServiceAction(ModelServiceAction):
15+
class DeleteModelServiceAction(ModelServiceSingleEntityAction):
1216
service_id: uuid.UUID
1317

14-
@override
15-
def entity_id(self) -> str | None:
16-
return None
17-
1818
@override
1919
@classmethod
2020
def operation_type(cls) -> ActionOperationType:
2121
return ActionOperationType.DELETE
2222

23+
@override
24+
def target_entity_id(self) -> str:
25+
return str(self.service_id)
26+
27+
@override
28+
def target_element(self) -> RBACElementRef:
29+
return RBACElementRef(RBACElementType.MODEL_DEPLOYMENT, str(self.service_id))
30+
2331

2432
@dataclass
25-
class DeleteModelServiceActionResult(BaseActionResult):
26-
success: bool
33+
class DeleteModelServiceActionResult(ModelServiceSingleEntityActionResult):
34+
service_id: uuid.UUID
2735

2836
@override
29-
def entity_id(self) -> str | None:
30-
return None
37+
def target_entity_id(self) -> str:
38+
return str(self.service_id)

src/ai/backend/manager/services/model_serving/actions/delete_route.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,43 @@
22
from dataclasses import dataclass
33
from typing import override
44

5-
from ai.backend.common.data.permission.types import EntityType
6-
from ai.backend.manager.actions.action import BaseActionResult
5+
from ai.backend.common.data.permission.types import EntityType, RBACElementType
76
from ai.backend.manager.actions.types import ActionOperationType
8-
from ai.backend.manager.services.model_serving.actions.base import ModelServiceAction
7+
from ai.backend.manager.data.permission.types import RBACElementRef
8+
from ai.backend.manager.services.model_serving.actions.base import (
9+
ModelServiceSingleEntityAction,
10+
ModelServiceSingleEntityActionResult,
11+
)
912

1013

1114
@dataclass
12-
class DeleteRouteAction(ModelServiceAction):
15+
class DeleteRouteAction(ModelServiceSingleEntityAction):
1316
service_id: uuid.UUID
1417
route_id: uuid.UUID
1518

1619
@override
1720
@classmethod
1821
def entity_type(cls) -> EntityType:
19-
return EntityType.DEPLOYMENT_ROUTE
20-
21-
@override
22-
def entity_id(self) -> str | None:
23-
return None
22+
return EntityType.MODEL_DEPLOYMENT
2423

2524
@override
2625
@classmethod
2726
def operation_type(cls) -> ActionOperationType:
2827
return ActionOperationType.DELETE
2928

29+
@override
30+
def target_entity_id(self) -> str:
31+
return str(self.route_id)
32+
33+
@override
34+
def target_element(self) -> RBACElementRef:
35+
return RBACElementRef(RBACElementType.MODEL_DEPLOYMENT, str(self.service_id))
36+
3037

3138
@dataclass
32-
class DeleteRouteActionResult(BaseActionResult):
33-
success: bool
39+
class DeleteRouteActionResult(ModelServiceSingleEntityActionResult):
40+
route_id: uuid.UUID
3441

3542
@override
36-
def entity_id(self) -> str | None:
37-
return None
43+
def target_entity_id(self) -> str:
44+
return str(self.route_id)

src/ai/backend/manager/services/model_serving/actions/get_model_service_info.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,38 @@
22
from dataclasses import dataclass
33
from typing import override
44

5-
from ai.backend.manager.actions.action import BaseActionResult
5+
from ai.backend.common.data.permission.types import RBACElementType
66
from ai.backend.manager.actions.types import ActionOperationType
77
from ai.backend.manager.data.model_serving.types import ServiceInfo
8-
from ai.backend.manager.services.model_serving.actions.base import ModelServiceAction
8+
from ai.backend.manager.data.permission.types import RBACElementRef
9+
from ai.backend.manager.services.model_serving.actions.base import (
10+
ModelServiceSingleEntityAction,
11+
ModelServiceSingleEntityActionResult,
12+
)
913

1014

1115
@dataclass
12-
class GetModelServiceInfoAction(ModelServiceAction):
16+
class GetModelServiceInfoAction(ModelServiceSingleEntityAction):
1317
service_id: uuid.UUID
1418

15-
@override
16-
def entity_id(self) -> str | None:
17-
return None
18-
1919
@override
2020
@classmethod
2121
def operation_type(cls) -> ActionOperationType:
2222
return ActionOperationType.GET
2323

24+
@override
25+
def target_entity_id(self) -> str:
26+
return str(self.service_id)
27+
28+
@override
29+
def target_element(self) -> RBACElementRef:
30+
return RBACElementRef(RBACElementType.MODEL_DEPLOYMENT, str(self.service_id))
31+
2432

2533
@dataclass
26-
class GetModelServiceInfoActionResult(BaseActionResult):
34+
class GetModelServiceInfoActionResult(ModelServiceSingleEntityActionResult):
2735
data: ServiceInfo
2836

2937
@override
30-
def entity_id(self) -> str | None:
38+
def target_entity_id(self) -> str:
3139
return str(self.data.endpoint_id)

src/ai/backend/manager/services/model_serving/actions/modify_endpoint.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,43 @@
22
from dataclasses import dataclass
33
from typing import override
44

5-
from ai.backend.manager.actions.action import BaseActionResult
5+
from ai.backend.common.data.permission.types import RBACElementType
66
from ai.backend.manager.actions.types import ActionOperationType
77
from ai.backend.manager.data.model_serving.types import EndpointData
8+
from ai.backend.manager.data.permission.types import RBACElementRef
89
from ai.backend.manager.models.endpoint import EndpointRow
910
from ai.backend.manager.repositories.base.updater import Updater
10-
from ai.backend.manager.services.model_serving.actions.base import ModelServiceAction
11+
from ai.backend.manager.services.model_serving.actions.base import (
12+
ModelServiceSingleEntityAction,
13+
ModelServiceSingleEntityActionResult,
14+
)
1115

1216

1317
@dataclass
14-
class ModifyEndpointAction(ModelServiceAction):
18+
class ModifyEndpointAction(ModelServiceSingleEntityAction):
1519
endpoint_id: uuid.UUID
1620
updater: Updater[EndpointRow]
1721

18-
@override
19-
def entity_id(self) -> str | None:
20-
return None
21-
2222
@override
2323
@classmethod
2424
def operation_type(cls) -> ActionOperationType:
2525
return ActionOperationType.UPDATE
2626

27+
@override
28+
def target_entity_id(self) -> str:
29+
return str(self.endpoint_id)
30+
31+
@override
32+
def target_element(self) -> RBACElementRef:
33+
return RBACElementRef(RBACElementType.MODEL_DEPLOYMENT, str(self.endpoint_id))
34+
2735

2836
@dataclass
29-
class ModifyEndpointActionResult(BaseActionResult):
37+
class ModifyEndpointActionResult(ModelServiceSingleEntityActionResult):
38+
endpoint_id: uuid.UUID
3039
success: bool
3140
data: EndpointData | None
3241

3342
@override
34-
def entity_id(self) -> str | None:
35-
return str(self.data.id) if self.data is not None else None
43+
def target_entity_id(self) -> str:
44+
return str(self.endpoint_id)

src/ai/backend/manager/services/model_serving/actions/update_route.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,44 @@
22
from dataclasses import dataclass
33
from typing import override
44

5-
from ai.backend.common.data.permission.types import EntityType
6-
from ai.backend.manager.actions.action import BaseActionResult
5+
from ai.backend.common.data.permission.types import EntityType, RBACElementType
76
from ai.backend.manager.actions.types import ActionOperationType
8-
from ai.backend.manager.services.model_serving.actions.base import ModelServiceAction
7+
from ai.backend.manager.data.permission.types import RBACElementRef
8+
from ai.backend.manager.services.model_serving.actions.base import (
9+
ModelServiceSingleEntityAction,
10+
ModelServiceSingleEntityActionResult,
11+
)
912

1013

1114
@dataclass
12-
class UpdateRouteAction(ModelServiceAction):
15+
class UpdateRouteAction(ModelServiceSingleEntityAction):
1316
service_id: uuid.UUID
1417
route_id: uuid.UUID
1518
traffic_ratio: float
1619

1720
@override
1821
@classmethod
1922
def entity_type(cls) -> EntityType:
20-
return EntityType.DEPLOYMENT_ROUTE
21-
22-
@override
23-
def entity_id(self) -> str | None:
24-
return None
23+
return EntityType.MODEL_DEPLOYMENT
2524

2625
@override
2726
@classmethod
2827
def operation_type(cls) -> ActionOperationType:
2928
return ActionOperationType.UPDATE
3029

30+
@override
31+
def target_entity_id(self) -> str:
32+
return str(self.route_id)
33+
34+
@override
35+
def target_element(self) -> RBACElementRef:
36+
return RBACElementRef(RBACElementType.MODEL_DEPLOYMENT, str(self.service_id))
37+
3138

3239
@dataclass
33-
class UpdateRouteActionResult(BaseActionResult):
34-
success: bool
40+
class UpdateRouteActionResult(ModelServiceSingleEntityActionResult):
41+
route_id: uuid.UUID
3542

3643
@override
37-
def entity_id(self) -> str | None:
38-
return None
44+
def target_entity_id(self) -> str:
45+
return str(self.route_id)

0 commit comments

Comments
 (0)