Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/9721.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add service/repository layer for permission update
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,28 @@ async def delete_permission(
raise ObjectNotFound(f"Permission with ID {purger.pk_value} does not exist.")
return result.row

async def update_permission(
self,
updater: Updater[PermissionRow],
) -> PermissionRow:
"""
Update a permission.

Args:
updater: Updater with permission ID and fields to update

Returns:
Updated permission row

Raises:
ObjectNotFound: If permission does not exist
"""
async with self._db.begin_session() as db_session:
result = await execute_updater(db_session, updater)
if result is None:
raise ObjectNotFound(f"Permission with ID {updater.pk_value} does not exist.")
return result.row

async def delete_object_permission(
self,
purger: Purger[ObjectPermissionRow],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,22 @@ async def delete_permission(
row = await self._db_source.delete_permission(purger)
return row.to_data()

@permission_controller_repository_resilience.apply()
async def update_permission(
self,
updater: Updater[PermissionRow],
) -> PermissionData:
"""
Update a permission in the database.

Returns the updated permission data.

Raises:
ObjectNotFound: If permission does not exist.
"""
row = await self._db_source.update_permission(updater)
return row.to_data()

@permission_controller_repository_resilience.apply()
async def create_object_permission(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
from typing import Any, override

from ai.backend.manager.data.permission.status import RoleStatus
from ai.backend.manager.data.permission.types import RoleSource
from ai.backend.manager.data.permission.types import (
EntityType,
OperationType,
RoleSource,
ScopeType,
)
from ai.backend.manager.models.rbac_models.permission.permission import PermissionRow
from ai.backend.manager.models.rbac_models.role import RoleRow
from ai.backend.manager.repositories.base.updater import UpdaterSpec
from ai.backend.manager.types import OptionalState, TriState
Expand Down Expand Up @@ -32,3 +38,27 @@ def build_values(self) -> dict[str, Any]:
self.status.update_dict(to_update, "status")
self.description.update_dict(to_update, "description")
return to_update


@dataclass
class PermissionUpdaterSpec(UpdaterSpec[PermissionRow]):
"""UpdaterSpec for permission updates."""

scope_type: OptionalState[ScopeType] = field(default_factory=OptionalState.nop)
scope_id: OptionalState[str] = field(default_factory=OptionalState.nop)
entity_type: OptionalState[EntityType] = field(default_factory=OptionalState.nop)
operation: OptionalState[OperationType] = field(default_factory=OptionalState.nop)

@property
@override
def row_class(self) -> type[PermissionRow]:
return PermissionRow

@override
def build_values(self) -> dict[str, Any]:
to_update: dict[str, Any] = {}
self.scope_type.update_dict(to_update, "scope_type")
self.scope_id.update_dict(to_update, "scope_id")
self.entity_type.update_dict(to_update, "entity_type")
self.operation.update_dict(to_update, "operation")
return to_update
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
SearchUsersAssignedToRoleAction,
SearchUsersAssignedToRoleActionResult,
)
from .update_permission import UpdatePermissionAction, UpdatePermissionActionResult
from .update_role import UpdateRoleAction, UpdateRoleActionResult
from .update_role_permissions import (
UpdateRolePermissionsAction,
Expand Down Expand Up @@ -47,6 +48,8 @@
"SearchPermissionsActionResult",
"SearchUsersAssignedToRoleAction",
"SearchUsersAssignedToRoleActionResult",
"UpdatePermissionAction",
"UpdatePermissionActionResult",
"UpdateRoleAction",
"UpdateRoleActionResult",
"UpdateRolePermissionsAction",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from dataclasses import dataclass
from typing import override

from ai.backend.manager.actions.action import BaseActionResult
from ai.backend.manager.actions.types import ActionOperationType
from ai.backend.manager.data.permission.permission import PermissionData
from ai.backend.manager.models.rbac_models.permission.permission import PermissionRow
from ai.backend.manager.repositories.base.updater import Updater
from ai.backend.manager.services.permission_contoller.actions.permission import PermissionAction


@dataclass
class UpdatePermissionAction(PermissionAction):
updater: Updater[PermissionRow]

@override
def entity_id(self) -> str | None:
return str(self.updater.pk_value)

@override
@classmethod
def operation_type(cls) -> ActionOperationType:
return ActionOperationType.UPDATE


@dataclass
class UpdatePermissionActionResult(BaseActionResult):
data: PermissionData

@override
def entity_id(self) -> str | None:
return str(self.data.id) if self.data else None
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
SearchScopesAction,
SearchScopesActionResult,
)
from .actions.update_permission import (
UpdatePermissionAction,
UpdatePermissionActionResult,
)
from .service import PermissionControllerService


Expand Down Expand Up @@ -83,6 +87,7 @@ class PermissionControllerProcessors(AbstractProcessorPackage):
]
search_permissions: ActionProcessor[SearchPermissionsAction, SearchPermissionsActionResult]
create_permission: ActionProcessor[CreatePermissionAction, CreatePermissionActionResult]
update_permission: ActionProcessor[UpdatePermissionAction, UpdatePermissionActionResult]
delete_permission: ActionProcessor[DeletePermissionAction, DeletePermissionActionResult]

def __init__(
Expand Down Expand Up @@ -111,6 +116,7 @@ def __init__(
)
self.search_permissions = ActionProcessor(service.search_permissions, action_monitors)
self.create_permission = ActionProcessor(service.create_permission, action_monitors)
self.update_permission = ActionProcessor(service.update_permission, action_monitors)
self.delete_permission = ActionProcessor(service.delete_permission, action_monitors)

@override
Expand All @@ -133,5 +139,6 @@ def supported_actions(self) -> list[ActionSpec]:
SearchElementAssociationsAction.spec(),
SearchPermissionsAction.spec(),
CreatePermissionAction.spec(),
UpdatePermissionAction.spec(),
DeletePermissionAction.spec(),
]
13 changes: 13 additions & 0 deletions src/ai/backend/manager/services/permission_contoller/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@
SearchUsersAssignedToRoleAction,
SearchUsersAssignedToRoleActionResult,
)
from ai.backend.manager.services.permission_contoller.actions.update_permission import (
UpdatePermissionAction,
UpdatePermissionActionResult,
)
from ai.backend.manager.services.permission_contoller.actions.update_role import (
UpdateRoleAction,
UpdateRoleActionResult,
Expand Down Expand Up @@ -129,6 +133,15 @@ async def delete_permission(
result = await self._repository.delete_permission(action.purger)
return DeletePermissionActionResult(data=result)

async def update_permission(
self, action: UpdatePermissionAction
) -> UpdatePermissionActionResult:
"""
Updates an existing permission in the repository.
"""
result = await self._repository.update_permission(action.updater)
return UpdatePermissionActionResult(data=result)

async def create_object_permission(
self, action: CreateObjectPermissionAction
) -> CreateObjectPermissionActionResult:
Expand Down
Loading