Skip to content

Commit

Permalink
chore: update GraphQL schema dump
Browse files Browse the repository at this point in the history
  • Loading branch information
fregataa committed May 17, 2024
1 parent cdfc4d6 commit fe8b91f
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 35 deletions.
3 changes: 3 additions & 0 deletions src/ai/backend/manager/api/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,9 @@ type VirtualFolder implements Item {
group: UUID
group_name: String
creator: String

"""Added in 24.09.0."""
domain_name: String
unmanaged_path: String
usage_mode: String
permission: String
Expand Down
41 changes: 35 additions & 6 deletions src/ai/backend/manager/models/acl.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,23 @@ class AbstractScopePermissionMap(Generic[ACLPermissionType, ACLObjectType], meta
project: Mapping[uuid.UUID, frozenset[ACLPermissionType]]
domain: Mapping[str, frozenset[ACLPermissionType]]

def apply_permission_filter(self, permission_to_include: ACLPermissionType) -> None:
self.user = {
uid: permissions
for uid, permissions in self.user.items()
if permission_to_include in permissions
}
self.project = {
pid: permissions
for pid, permissions in self.project.items()
if permission_to_include in permissions
}
self.domain = {
dname: permissions
for dname, permissions in self.domain.items()
if permission_to_include in permissions
}

@abstractmethod
async def determine_permission(self, acl_obj: ACLObjectType) -> frozenset[ACLPermissionType]:
pass
Expand All @@ -100,7 +117,7 @@ async def determine_permission(self, acl_obj: ACLObjectType) -> frozenset[ACLPer
ScopePermissionMapType = TypeVar("ScopePermissionMapType", bound=AbstractScopePermissionMap)


class AbstractScopePermissionMapBuilder(Generic[ScopePermissionMapType], metaclass=ABCMeta):
class AbstractScopePermissionMapFactory(Generic[ScopePermissionMapType], metaclass=ABCMeta):
@classmethod
async def build(
cls,
Expand All @@ -110,14 +127,26 @@ async def build(
) -> ScopePermissionMapType:
match requested_scope:
case RequestedUserScope(user_id=user_id):
return await cls._build_in_user_scope(db_session, ctx, user_id)
result = await cls._build_in_user_scope(
db_session,
ctx,
user_id,
)
case RequestedProjectScope(project_id=project_id):
return await cls._build_in_project_scope(db_session, ctx, project_id)
result = await cls._build_in_project_scope(
db_session,
ctx,
project_id,
)
case RequestedDomainScope(domain_name=domain_name):
return await cls._build_in_domain_scope(db_session, ctx, domain_name)
result = await cls._build_in_domain_scope(
db_session,
ctx,
domain_name,
)
case _:
pass
raise RuntimeError(f"invalid request scope {requested_scope}")
raise RuntimeError(f"invalid request scope {requested_scope}")
return result

@classmethod
@abstractmethod
Expand Down
72 changes: 43 additions & 29 deletions src/ai/backend/manager/models/vfolder.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
from .acl import (
AbstractACLPermission,
AbstractScopePermissionMap,
AbstractScopePermissionMapBuilder,
AbstractScopePermissionMapFactory,
RequestedScope,
RequesterContext,
)
Expand Down Expand Up @@ -127,7 +127,7 @@
"VFolderACLObject",
"OWNER_PERMISSIONS",
"ScopePermissionMap",
"ScopePermissionMapBuilder",
"ScopePermissionMapFactory",
)


Expand Down Expand Up @@ -742,10 +742,23 @@ class ScopePermissionMap(AbstractScopePermissionMap[VFolderACLPermission, VFolde
additional: Mapping[
uuid.UUID, frozenset[VFolderACLPermission]
] # Key: vfolder id / Value: set of VFolderACLPermissions
overriden: Mapping[
overridden: Mapping[
uuid.UUID, frozenset[VFolderACLPermission]
] # Key: vfolder id / Value: set of VFolderACLPermissions

def apply_permission_filter(self, permission_to_include: VFolderACLPermission) -> None:
super().apply_permission_filter(permission_to_include)
self.additional = {
vfid: permissions
for vfid, permissions in self.additional.items()
if permission_to_include in permissions
}
self.overridden = {
vfid: permissions
for vfid, permissions in self.overridden.items()
if permission_to_include in permissions
}

@property
def query_condition(self) -> WhereClauseType | None:
cond: WhereClauseType | None = None
Expand All @@ -764,8 +777,8 @@ def _coalesce(
cond = _coalesce(cond, VFolderRow.domain_name.in_(self.domain.keys()))
if self.additional:
cond = _coalesce(cond, VFolderRow.id.in_(self.additional.keys()))
if self.overriden:
cond = _coalesce(cond, VFolderRow.id.in_(self.overriden.keys()))
if self.overridden:
cond = _coalesce(cond, VFolderRow.id.in_(self.overridden.keys()))
return cond

@property
Expand All @@ -777,8 +790,8 @@ def query_stmt(self) -> sa.sql.Select | None:

async def determine_permission(self, acl_obj: VFolderRow) -> frozenset[VFolderACLPermission]:
vfolder_row = acl_obj
if (vfolder_id := acl_obj.id) in self.overriden:
return self.overriden[vfolder_id]
if (vfolder_id := acl_obj.id) in self.overridden:
return self.overridden[vfolder_id]
permissions: set[VFolderACLPermission] = set()
permissions |= self.additional.get(vfolder_id, set())
permissions |= self.user.get(vfolder_row.user, set())
Expand All @@ -787,7 +800,7 @@ async def determine_permission(self, acl_obj: VFolderRow) -> frozenset[VFolderAC
return frozenset(permissions)


class ScopePermissionMapBuilder(AbstractScopePermissionMapBuilder[ScopePermissionMap]):
class ScopePermissionMapFactory(AbstractScopePermissionMapFactory[ScopePermissionMap]):
@classmethod
async def _build_in_user_scope(
cls,
Expand All @@ -799,7 +812,7 @@ async def _build_in_user_scope(
project: Mapping[uuid.UUID, frozenset[VFolderACLPermission]] = {}
domain: Mapping[str, frozenset[VFolderACLPermission]] = {}
additional: Mapping[uuid.UUID, frozenset[VFolderACLPermission]] = {}
overriden: Mapping[uuid.UUID, frozenset[VFolderACLPermission]] = {}
overridden: Mapping[uuid.UUID, frozenset[VFolderACLPermission]] = {}
match ctx.user_role:
case UserRole.SUPERADMIN:
if ctx.user_id == user_id:
Expand Down Expand Up @@ -871,7 +884,7 @@ async def _build_in_user_scope(
user = {user_id: DEFAULT_ADMIN_PERMISSIONS_ON_USER_VFOLDERS}
case UserRole.USER:
if ctx.user_id == user_id:
overriden_stmt = (
overridden_stmt = (
sa.select(VFolderPermissionRow)
.select_from(sa.join(VFolderPermissionRow, VFolderRow))
.where(
Expand All @@ -881,9 +894,9 @@ async def _build_in_user_scope(
) # filter out project vfolders
)
)
overriden = {
overridden = {
row.vfolder: PERMISSION_TO_ACL_PERMISSION_MAP[row.permission]
for row in await db_session.scalars(overriden_stmt)
for row in await db_session.scalars(overridden_stmt)
}

user = {ctx.user_id: OWNER_PERMISSIONS}
Expand All @@ -894,7 +907,7 @@ async def _build_in_user_scope(
project=project,
domain=domain,
additional=additional,
overriden=overriden,
overridden=overridden,
)

@classmethod
Expand All @@ -908,7 +921,7 @@ async def _build_in_project_scope(
project: Mapping[uuid.UUID, frozenset[VFolderACLPermission]] = {}
domain: Mapping[str, frozenset[VFolderACLPermission]] = {}
additional: Mapping[uuid.UUID, frozenset[VFolderACLPermission]] = {}
overriden: Mapping[uuid.UUID, frozenset[VFolderACLPermission]] = {}
overridden: Mapping[uuid.UUID, frozenset[VFolderACLPermission]] = {}

project_ctx = await ctx.get_or_init_project_ctx()
role_in_project = project_ctx.get(project_id)
Expand Down Expand Up @@ -986,7 +999,7 @@ async def _build_in_project_scope(
}

# Admin/user is associated with the project, fetch owned/invited vfolders
overriden_stmt = (
overridden_stmt = (
sa.select(VFolderPermissionRow)
.select_from(sa.join(VFolderPermissionRow, VFolderRow))
.where(
Expand All @@ -998,9 +1011,9 @@ async def _build_in_project_scope(
# project vfolders or invited user vfolders
)
)
overriden = {
overridden = {
row.vfolder: PERMISSION_TO_ACL_PERMISSION_MAP[row.permission]
for row in await db_session.scalars(overriden_stmt)
for row in await db_session.scalars(overridden_stmt)
}

user = {ctx.user_id: OWNER_PERMISSIONS}
Expand All @@ -1014,7 +1027,7 @@ async def _build_in_project_scope(
project=project,
domain=domain,
additional=additional,
overriden=overriden,
overridden=overridden,
)

@classmethod
Expand All @@ -1028,7 +1041,7 @@ async def _build_in_domain_scope(
project: Mapping[uuid.UUID, frozenset[VFolderACLPermission]] = {}
domain: Mapping[str, frozenset[VFolderACLPermission]] = {}
additional: Mapping[uuid.UUID, frozenset[VFolderACLPermission]] = {}
overriden: Mapping[uuid.UUID, frozenset[VFolderACLPermission]] = {}
overridden: Mapping[uuid.UUID, frozenset[VFolderACLPermission]] = {}
match ctx.user_role:
case UserRole.SUPERADMIN:
domain = {
Expand All @@ -1044,7 +1057,7 @@ async def _build_in_domain_scope(
project=project,
domain=domain,
additional=additional,
overriden=overriden,
overridden=overridden,
)
project_ctx = await ctx.get_or_init_project_ctx()
project = {
Expand Down Expand Up @@ -1088,7 +1101,7 @@ async def _build_in_domain_scope(
project=project,
domain=domain,
additional=additional,
overriden=overriden,
overridden=overridden,
)
project_ctx = await ctx.get_or_init_project_ctx()
project = {
Expand Down Expand Up @@ -1140,7 +1153,7 @@ async def _build_in_domain_scope(
project=project,
domain=domain,
additional=additional,
overriden=overriden,
overridden=overridden,
)
project_ctx = await ctx.get_or_init_project_ctx()
project = {
Expand All @@ -1149,17 +1162,17 @@ async def _build_in_domain_scope(
else OWNER_PERMISSIONS
for project_id, role in project_ctx.items()
}
overriden_stmt = (
overridden_stmt = (
sa.select(VFolderPermissionRow)
.select_from(sa.join(VFolderPermissionRow, VFolderRow))
.where(
(VFolderPermissionRow.user == ctx.user_id)
& (VFolderRow.domain_name == domain_name)
)
)
overriden = {
overridden = {
row.vfolder: PERMISSION_TO_ACL_PERMISSION_MAP[row.permission]
for row in await db_session.scalars(overriden_stmt)
for row in await db_session.scalars(overridden_stmt)
}

user = {ctx.user_id: OWNER_PERMISSIONS}
Expand All @@ -1171,7 +1184,7 @@ async def _build_in_domain_scope(
project=project,
domain=domain,
additional=additional,
overriden=overriden,
overridden=overridden,
)


Expand All @@ -1192,7 +1205,9 @@ async def get_vfolders(
blocked_status: Container[VFolderOperationStatus] | None = None,
) -> list[VFolderACLObject]:
async with SASession(ctx.db_conn) as db_session:
scope_ctx = await ScopePermissionMapBuilder.build(db_session, ctx, requested_scope)
scope_ctx = await ScopePermissionMapFactory.build(db_session, ctx, requested_scope)
if requested_permission is not None:
scope_ctx.apply_permission_filter(requested_permission)
stmt = scope_ctx.query_stmt
if stmt is None:
return []
Expand All @@ -1210,8 +1225,7 @@ async def get_vfolders(
result: list[VFolderACLObject] = []
for row in await db_session.scalars(stmt):
permissions = await scope_ctx.determine_permission(row)
if requested_permission is None or requested_permission in permissions:
result.append(VFolderACLObject(row, permissions))
result.append(VFolderACLObject(row, permissions))
return result


Expand Down

0 comments on commit fe8b91f

Please sign in to comment.