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/10029.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Apply RBAC validators to project (group) action processors for proper permission enforcement
26 changes: 26 additions & 0 deletions src/ai/backend/manager/services/group/actions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,29 @@ def field_data(self) -> FieldData | None:

class GroupSingleEntityActionResult(BaseSingleEntityActionResult):
pass


class ProjectScopeAction(BaseScopeAction):
@override
@classmethod
def entity_type(cls) -> EntityType:
return EntityType.PROJECT


class ProjectScopeActionResult(BaseScopeActionResult):
pass


class ProjectSingleEntityAction(BaseSingleEntityAction):
@override
@classmethod
def entity_type(cls) -> EntityType:
return EntityType.PROJECT

@override
def field_data(self) -> FieldData | None:
return None


class ProjectSingleEntityActionResult(BaseSingleEntityActionResult):
pass
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
)
from ai.backend.manager.services.group.actions.base import (
GroupAction,
GroupScopeAction,
GroupScopeActionResult,
GroupSingleEntityAction,
GroupSingleEntityActionResult,
ProjectScopeAction,
ProjectScopeActionResult,
ProjectSingleEntityAction,
ProjectSingleEntityActionResult,
)


Expand All @@ -42,7 +42,7 @@ def operation_type(cls) -> ActionOperationType:


@dataclass
class SearchProjectsByDomainAction(GroupScopeAction):
class SearchProjectsByDomainAction(ProjectScopeAction):
"""Search projects within a domain."""

scope: DomainProjectSearchScope
Expand All @@ -67,7 +67,7 @@ def target_element(self) -> RBACElementRef:


@dataclass
class SearchProjectsByUserAction(GroupScopeAction):
class SearchProjectsByUserAction(ProjectScopeAction):
"""Search projects a user is member of."""

scope: UserProjectSearchScope
Expand All @@ -92,7 +92,7 @@ def target_element(self) -> RBACElementRef:


@dataclass
class GetProjectAction(GroupSingleEntityAction):
class GetProjectAction(ProjectSingleEntityAction):
"""Get a single project by UUID."""

project_id: UUID
Expand Down Expand Up @@ -129,7 +129,7 @@ def entity_id(self) -> str | None:


@dataclass
class ScopedSearchProjectsActionResult(GroupScopeActionResult):
class ScopedSearchProjectsActionResult(ProjectScopeActionResult):
"""Result from searching projects within a scope."""

items: list[GroupData]
Expand All @@ -149,7 +149,7 @@ def scope_id(self) -> str:


@dataclass
class GetProjectActionResult(GroupSingleEntityActionResult):
class GetProjectActionResult(ProjectSingleEntityActionResult):
"""Result from getting a single project."""

data: GroupData
Expand Down
22 changes: 17 additions & 5 deletions src/ai/backend/manager/services/group/processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,20 @@ def __init__(
action_monitors: list[ActionMonitor],
validators: ActionValidators,
) -> None:
self.create_group = ScopeActionProcessor(group_service.create_group, action_monitors)
self.modify_group = SingleEntityActionProcessor(group_service.modify_group, action_monitors)
self.delete_group = SingleEntityActionProcessor(group_service.delete_group, action_monitors)
self.purge_group = SingleEntityActionProcessor(group_service.purge_group, action_monitors)
rbac_scope_validators = [validators.rbac.scope]
rbac_single_entity_validators = [validators.rbac.single_entity]
self.create_group = ScopeActionProcessor(
group_service.create_group, action_monitors, validators=rbac_scope_validators
)
self.modify_group = SingleEntityActionProcessor(
group_service.modify_group, action_monitors, validators=rbac_single_entity_validators
)
self.delete_group = SingleEntityActionProcessor(
group_service.delete_group, action_monitors, validators=rbac_single_entity_validators
)
self.purge_group = SingleEntityActionProcessor(
group_service.purge_group, action_monitors, validators=rbac_single_entity_validators
)
self.usage_per_month = ActionProcessor(group_service.usage_per_month, action_monitors)
self.usage_per_period = ActionProcessor(group_service.usage_per_period, action_monitors)
self.search_projects = ActionProcessor(group_service.search_projects, action_monitors)
Expand All @@ -77,7 +87,9 @@ def __init__(
self.search_projects_by_user = ScopeActionProcessor(
group_service.search_projects_by_user, action_monitors
)
self.get_project = SingleEntityActionProcessor(group_service.get_project, action_monitors)
self.get_project = SingleEntityActionProcessor(
group_service.get_project, action_monitors, validators=rbac_single_entity_validators
)

@override
def supported_actions(self) -> list[ActionSpec]:
Expand Down
Loading