|
1 | 1 | import logging
|
2 | 2 | from abc import ABC, abstractmethod
|
3 | 3 | from collections.abc import Callable, Iterable, Sequence
|
| 4 | +from datetime import timedelta |
4 | 5 | from functools import cached_property
|
5 | 6 | from typing import Generic, TypeVar, final
|
6 | 7 |
|
| 8 | +from databricks.labs.blueprint.paths import WorkspacePath |
7 | 9 | from databricks.sdk import WorkspaceClient
|
8 |
| -from databricks.sdk.errors import NotFound |
9 |
| -from databricks.sdk.service.iam import User |
| 10 | +from databricks.sdk.errors import NotFound, InternalError |
| 11 | +from databricks.sdk.retries import retried |
| 12 | +from databricks.sdk.service.iam import User, PermissionLevel |
| 13 | +from databricks.sdk.service.workspace import ObjectType |
10 | 14 |
|
11 | 15 | logger = logging.getLogger(__name__)
|
12 | 16 |
|
@@ -190,3 +194,46 @@ def owner_of(self, record: Record) -> str:
|
190 | 194 | def _maybe_direct_owner(self, record: Record) -> str | None:
|
191 | 195 | """Obtain the record-specific user-name associated with the given record, if any."""
|
192 | 196 | return None
|
| 197 | + |
| 198 | + |
| 199 | +class WorkspacePathOwnership(Ownership[WorkspacePath]): |
| 200 | + def __init__(self, administrator_locator: AdministratorLocator, ws: WorkspaceClient) -> None: |
| 201 | + super().__init__(administrator_locator) |
| 202 | + self._ws = ws |
| 203 | + |
| 204 | + @retried(on=[InternalError], timeout=timedelta(minutes=1)) |
| 205 | + def _maybe_direct_owner(self, record: WorkspacePath) -> str | None: |
| 206 | + maybe_type_and_id = self._maybe_type_and_id(record) |
| 207 | + if not maybe_type_and_id: |
| 208 | + return None |
| 209 | + object_type, object_id = maybe_type_and_id |
| 210 | + try: |
| 211 | + object_permissions = self._ws.permissions.get(object_type, object_id) |
| 212 | + return self._infer_from_first_can_manage(object_permissions) |
| 213 | + except NotFound: |
| 214 | + logger.warning(f"removed on backend: {object_type} {object_id}") |
| 215 | + return None |
| 216 | + |
| 217 | + @staticmethod |
| 218 | + def _maybe_type_and_id(path: WorkspacePath) -> tuple[str, str] | None: |
| 219 | + object_info = path._object_info # pylint: disable=protected-access |
| 220 | + object_id = str(object_info.object_id) |
| 221 | + match object_info.object_type: |
| 222 | + case ObjectType.NOTEBOOK: |
| 223 | + return 'notebooks', object_id |
| 224 | + case ObjectType.FILE: |
| 225 | + return 'files', object_id |
| 226 | + return None |
| 227 | + |
| 228 | + @staticmethod |
| 229 | + def _infer_from_first_can_manage(object_permissions): |
| 230 | + for acl in object_permissions.access_control_list: |
| 231 | + for permission in acl.all_permissions: |
| 232 | + if permission.permission_level != PermissionLevel.CAN_MANAGE: |
| 233 | + continue |
| 234 | + if acl.user_name: |
| 235 | + return acl.user_name |
| 236 | + if acl.group_name: |
| 237 | + return acl.group_name |
| 238 | + return acl.service_principal_name |
| 239 | + return None |
0 commit comments