Skip to content

Commit 7c18773

Browse files
committed
feat: Add RBAC Global scope
1 parent 8f6bd3f commit 7c18773

File tree

12 files changed

+383
-18
lines changed

12 files changed

+383
-18
lines changed

changes/6004.feature.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add RBAC Global scope

src/ai/backend/manager/data/permission/id.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ class ScopeId:
99
scope_type: ScopeType
1010
scope_id: str
1111

12+
def __hash__(self) -> int:
13+
return hash((self.scope_type, self.scope_id))
14+
1215
@classmethod
1316
def from_str(cls, val: str) -> Self:
1417
scope_type, _, scope_id = val.partition(":")
@@ -23,6 +26,9 @@ class ObjectId:
2326
entity_type: EntityType
2427
entity_id: str
2528

29+
def __hash__(self) -> int:
30+
return hash((self.entity_type, self.entity_id))
31+
2632
@classmethod
2733
def from_str(cls, val: str) -> Self:
2834
entity_type, _, entity_id = val.partition(":")

src/ai/backend/manager/data/permission/permission_group.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from dataclasses import dataclass
33

44
from .id import ScopeId
5+
from .permission import PermissionData
56

67

78
@dataclass
@@ -26,3 +27,5 @@ class PermissionGroupData:
2627
id: uuid.UUID
2728
role_id: uuid.UUID
2829
scope_id: ScopeId
30+
31+
permissions: list[PermissionData]

src/ai/backend/manager/data/permission/role.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import uuid
24
from dataclasses import dataclass, field
35
from datetime import datetime
@@ -10,7 +12,7 @@
1012
ObjectPermissionCreateInputBeforeRoleCreation,
1113
ObjectPermissionData,
1214
)
13-
from .permission_group import PermissionGroupCreatorBeforeRoleCreation
15+
from .permission_group import PermissionGroupCreatorBeforeRoleCreation, PermissionGroupData
1416
from .status import RoleStatus
1517
from .types import EntityType, OperationType, RoleSource
1618

@@ -71,6 +73,7 @@ class RoleDataWithPermissions:
7173
source: RoleSource
7274
status: RoleStatus
7375

76+
permission_groups: list[PermissionGroupData]
7477
object_permissions: list[ObjectPermissionData]
7578

7679
created_at: datetime
@@ -94,6 +97,28 @@ class SingleEntityPermissionCheckInput:
9497
operation: OperationType
9598

9699

100+
@dataclass
101+
class BatchEntityPermissionCheckInput:
102+
user_id: uuid.UUID
103+
target_object_ids: list[ObjectId]
104+
operation: OperationType
105+
106+
107+
@dataclass
108+
class ScopePermissionSet:
109+
scope_id: ScopeId
110+
scope_permissions: set[OperationType]
111+
global_permissions: Optional[set[OperationType]]
112+
113+
114+
@dataclass
115+
class ObjectPermissionSet:
116+
object_id: ObjectId
117+
object_permissions: set[OperationType]
118+
mapped_scopes: dict[ScopeId, set[OperationType]]
119+
global_permissions: Optional[set[OperationType]]
120+
121+
97122
@dataclass
98123
class UserRoleAssignmentInput:
99124
"""

src/ai/backend/manager/data/permission/types.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,8 @@ class ScopeType(enum.StrEnum):
118118
DOMAIN = "domain"
119119
PROJECT = "project"
120120
USER = "user"
121+
122+
GLOBAL = "global" # Global scope
123+
124+
125+
GLOBAL_SCOPE_ID = "global"

src/ai/backend/manager/models/rbac_models/association_scopes_entities.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,6 @@ class AssociationScopesEntitiesRow(Base):
5151
)
5252

5353
def object_id(self) -> ObjectId:
54-
"""
55-
Convert the association to a tuple of ScopeId and ObjectId.
56-
"""
5754
return ObjectId(entity_type=self.entity_type, entity_id=self.entity_id)
5855

5956
def parsed_scope_id(self) -> ScopeId:

src/ai/backend/manager/models/rbac_models/migration/enums.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import enum
24

35
from ai.backend.manager.data.permission.status import (
@@ -54,23 +56,23 @@ def to_original(self) -> OriginalOperationType:
5456
return OriginalOperationType(self.value)
5557

5658
@classmethod
57-
def owner_operations(cls) -> set["OperationType"]:
59+
def owner_operations(cls) -> set[OperationType]:
5860
"""
5961
Returns a set of operations that are considered owner operations.
6062
Owner operations are those that allow full control over an entity.
6163
"""
6264
return {op for op in cls}
6365

6466
@classmethod
65-
def admin_operations(cls) -> set["OperationType"]:
67+
def admin_operations(cls) -> set[OperationType]:
6668
"""
6769
Returns a set of operations that are considered admin operations.
6870
Admin operations are those that allow management of entities, including creation and deletion.
6971
"""
7072
return {op for op in cls}
7173

7274
@classmethod
73-
def member_operations(cls) -> set["OperationType"]:
75+
def member_operations(cls) -> set[OperationType]:
7476
"""
7577
Returns a set of operations that are considered member operations.
7678
Member operations are those that allow read access.
@@ -85,6 +87,8 @@ class ScopeType(enum.StrEnum):
8587
PROJECT = "project"
8688
USER = "user"
8789

90+
GLOBAL = "global"
91+
8892
def to_original(self) -> OriginalScopeType:
8993
return OriginalScopeType(self.value)
9094

src/ai/backend/manager/models/rbac_models/migration/types.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
from typing import Any
44

55
from ai.backend.manager.data.permission.id import ObjectId, ScopeId
6+
from ai.backend.manager.data.permission.types import GLOBAL_SCOPE_ID
67

78
from .enums import RoleSource, RoleStatus, ScopeType
89

910
ROLE_NAME_PREFIX = "role_"
1011
ADMIN_ROLE_NAME_SUFFIX = "_admin"
1112

1213

14+
GLOBAL_SCOPE_ID = GLOBAL_SCOPE_ID
15+
16+
1317
@dataclass
1418
class UserRoleCreateInput:
1519
user_id: uuid.UUID

src/ai/backend/manager/models/rbac_models/permission/permission_group.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
)
2424

2525
if TYPE_CHECKING:
26+
from ..association_scopes_entities import AssociationScopesEntitiesRow
2627
from ..role import RoleRow
2728
from .permission import PermissionRow
2829

@@ -45,6 +46,11 @@ class PermissionGroupRow(Base):
4546
back_populates="permission_group_rows",
4647
primaryjoin="RoleRow.id == foreign(PermissionGroupRow.role_id)",
4748
)
49+
mapped_entities: list[AssociationScopesEntitiesRow] = relationship(
50+
"AssociationScopesEntitiesRow",
51+
primaryjoin="PermissionGroupRow.scope_id == foreign(AssociationScopesEntitiesRow.scope_id)",
52+
viewonly=True,
53+
)
4854
permission_rows: list[PermissionRow] = relationship(
4955
"PermissionRow",
5056
back_populates="permission_group_row",
@@ -67,4 +73,5 @@ def to_data(self) -> PermissionGroupData:
6773
id=self.id,
6874
role_id=self.role_id,
6975
scope_id=ScopeId(scope_type=self.scope_type, scope_id=self.scope_id),
76+
permissions=[permission.to_data() for permission in self.permission_rows],
7077
)

src/ai/backend/manager/models/rbac_models/role.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from ai.backend.manager.data.permission.role import (
1717
RoleCreateInput,
1818
RoleData,
19+
RoleDataWithPermissions,
1920
)
2021
from ai.backend.manager.data.permission.status import (
2122
RoleStatus,
@@ -93,6 +94,20 @@ def to_data(self) -> RoleData:
9394
description=self.description,
9495
)
9596

97+
def to_data_with_permissions(self) -> RoleDataWithPermissions:
98+
return RoleDataWithPermissions(
99+
id=self.id,
100+
name=self.name,
101+
source=self.source,
102+
status=self.status,
103+
created_at=self.created_at,
104+
updated_at=self.updated_at,
105+
deleted_at=self.deleted_at,
106+
description=self.description,
107+
permission_groups=[pg_row.to_data() for pg_row in self.permission_group_rows],
108+
object_permissions=[op_row.to_data() for op_row in self.object_permission_rows],
109+
)
110+
96111
@classmethod
97112
def from_input(cls, data: RoleCreateInput) -> Self:
98113
return cls(

0 commit comments

Comments
 (0)