# Code generated from OpenAPI specs by Databricks SDK Generator. DO NOT EDIT. from __future__ import annotations import logging from dataclasses import dataclass from enum import Enum from typing import Any, Dict, Iterator, List, Optional from databricks.sdk.client_types import HostType from databricks.sdk.service._internal import (_enum, _from_dict, _repeated_dict, _repeated_enum) _LOG = logging.getLogger("databricks.sdk") # all definitions in this file are in alphabetical order @dataclass class AccessControlRequest: group_name: Optional[str] = None """name of the group""" permission_level: Optional[PermissionLevel] = None service_principal_name: Optional[str] = None """application ID of a service principal""" user_name: Optional[str] = None """name of the user""" def as_dict(self) -> dict: """Serializes the AccessControlRequest into a dictionary suitable for use as a JSON request body.""" body = {} if self.group_name is not None: body["group_name"] = self.group_name if self.permission_level is not None: body["permission_level"] = self.permission_level.value if self.service_principal_name is not None: body["service_principal_name"] = self.service_principal_name if self.user_name is not None: body["user_name"] = self.user_name return body def as_shallow_dict(self) -> dict: """Serializes the AccessControlRequest into a shallow dictionary of its immediate attributes.""" body = {} if self.group_name is not None: body["group_name"] = self.group_name if self.permission_level is not None: body["permission_level"] = self.permission_level if self.service_principal_name is not None: body["service_principal_name"] = self.service_principal_name if self.user_name is not None: body["user_name"] = self.user_name return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> AccessControlRequest: """Deserializes the AccessControlRequest from a dictionary.""" return cls( group_name=d.get("group_name", None), permission_level=_enum(d, "permission_level", PermissionLevel), service_principal_name=d.get("service_principal_name", None), user_name=d.get("user_name", None), ) @dataclass class AccessControlResponse: all_permissions: Optional[List[Permission]] = None """All permissions.""" display_name: Optional[str] = None """Display name of the user or service principal.""" group_name: Optional[str] = None """name of the group""" service_principal_name: Optional[str] = None """Name of the service principal.""" user_name: Optional[str] = None """name of the user""" def as_dict(self) -> dict: """Serializes the AccessControlResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.all_permissions: body["all_permissions"] = [v.as_dict() for v in self.all_permissions] if self.display_name is not None: body["display_name"] = self.display_name if self.group_name is not None: body["group_name"] = self.group_name if self.service_principal_name is not None: body["service_principal_name"] = self.service_principal_name if self.user_name is not None: body["user_name"] = self.user_name return body def as_shallow_dict(self) -> dict: """Serializes the AccessControlResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.all_permissions: body["all_permissions"] = self.all_permissions if self.display_name is not None: body["display_name"] = self.display_name if self.group_name is not None: body["group_name"] = self.group_name if self.service_principal_name is not None: body["service_principal_name"] = self.service_principal_name if self.user_name is not None: body["user_name"] = self.user_name return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> AccessControlResponse: """Deserializes the AccessControlResponse from a dictionary.""" return cls( all_permissions=_repeated_dict(d, "all_permissions", Permission), display_name=d.get("display_name", None), group_name=d.get("group_name", None), service_principal_name=d.get("service_principal_name", None), user_name=d.get("user_name", None), ) @dataclass class AccountGroup: account_id: Optional[str] = None """Databricks account ID""" display_name: Optional[str] = None """String that represents a human-readable group name""" external_id: Optional[str] = None """external_id should be unique for identifying groups""" id: Optional[str] = None """Databricks group ID""" members: Optional[List[ComplexValue]] = None meta: Optional[ResourceMeta] = None """Container for the group identifier. Workspace local versus account.""" roles: Optional[List[ComplexValue]] = None """Indicates if the group has the admin role.""" def as_dict(self) -> dict: """Serializes the AccountGroup into a dictionary suitable for use as a JSON request body.""" body = {} if self.account_id is not None: body["account_id"] = self.account_id if self.display_name is not None: body["displayName"] = self.display_name if self.external_id is not None: body["externalId"] = self.external_id if self.id is not None: body["id"] = self.id if self.members: body["members"] = [v.as_dict() for v in self.members] if self.meta: body["meta"] = self.meta.as_dict() if self.roles: body["roles"] = [v.as_dict() for v in self.roles] return body def as_shallow_dict(self) -> dict: """Serializes the AccountGroup into a shallow dictionary of its immediate attributes.""" body = {} if self.account_id is not None: body["account_id"] = self.account_id if self.display_name is not None: body["displayName"] = self.display_name if self.external_id is not None: body["externalId"] = self.external_id if self.id is not None: body["id"] = self.id if self.members: body["members"] = self.members if self.meta: body["meta"] = self.meta if self.roles: body["roles"] = self.roles return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> AccountGroup: """Deserializes the AccountGroup from a dictionary.""" return cls( account_id=d.get("account_id", None), display_name=d.get("displayName", None), external_id=d.get("externalId", None), id=d.get("id", None), members=_repeated_dict(d, "members", ComplexValue), meta=_from_dict(d, "meta", ResourceMeta), roles=_repeated_dict(d, "roles", ComplexValue), ) @dataclass class AccountServicePrincipal: account_id: Optional[str] = None """Databricks account ID""" active: Optional[bool] = None """If this user is active""" application_id: Optional[str] = None """UUID relating to the service principal""" display_name: Optional[str] = None """String that represents a concatenation of given and family names.""" external_id: Optional[str] = None id: Optional[str] = None """Databricks service principal ID.""" roles: Optional[List[ComplexValue]] = None """Indicates if the group has the admin role.""" def as_dict(self) -> dict: """Serializes the AccountServicePrincipal into a dictionary suitable for use as a JSON request body.""" body = {} if self.account_id is not None: body["account_id"] = self.account_id if self.active is not None: body["active"] = self.active if self.application_id is not None: body["applicationId"] = self.application_id if self.display_name is not None: body["displayName"] = self.display_name if self.external_id is not None: body["externalId"] = self.external_id if self.id is not None: body["id"] = self.id if self.roles: body["roles"] = [v.as_dict() for v in self.roles] return body def as_shallow_dict(self) -> dict: """Serializes the AccountServicePrincipal into a shallow dictionary of its immediate attributes.""" body = {} if self.account_id is not None: body["account_id"] = self.account_id if self.active is not None: body["active"] = self.active if self.application_id is not None: body["applicationId"] = self.application_id if self.display_name is not None: body["displayName"] = self.display_name if self.external_id is not None: body["externalId"] = self.external_id if self.id is not None: body["id"] = self.id if self.roles: body["roles"] = self.roles return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> AccountServicePrincipal: """Deserializes the AccountServicePrincipal from a dictionary.""" return cls( account_id=d.get("account_id", None), active=d.get("active", None), application_id=d.get("applicationId", None), display_name=d.get("displayName", None), external_id=d.get("externalId", None), id=d.get("id", None), roles=_repeated_dict(d, "roles", ComplexValue), ) @dataclass class AccountUser: account_id: Optional[str] = None """Databricks account ID""" active: Optional[bool] = None """If this user is active""" display_name: Optional[str] = None """String that represents a concatenation of given and family names. For example `John Smith`.""" emails: Optional[List[ComplexValue]] = None """All the emails associated with the Databricks user.""" external_id: Optional[str] = None """External ID is not currently supported. It is reserved for future use.""" id: Optional[str] = None """Databricks user ID.""" name: Optional[Name] = None roles: Optional[List[ComplexValue]] = None """Indicates if the group has the admin role.""" user_name: Optional[str] = None """Email address of the Databricks user.""" def as_dict(self) -> dict: """Serializes the AccountUser into a dictionary suitable for use as a JSON request body.""" body = {} if self.account_id is not None: body["account_id"] = self.account_id if self.active is not None: body["active"] = self.active if self.display_name is not None: body["displayName"] = self.display_name if self.emails: body["emails"] = [v.as_dict() for v in self.emails] if self.external_id is not None: body["externalId"] = self.external_id if self.id is not None: body["id"] = self.id if self.name: body["name"] = self.name.as_dict() if self.roles: body["roles"] = [v.as_dict() for v in self.roles] if self.user_name is not None: body["userName"] = self.user_name return body def as_shallow_dict(self) -> dict: """Serializes the AccountUser into a shallow dictionary of its immediate attributes.""" body = {} if self.account_id is not None: body["account_id"] = self.account_id if self.active is not None: body["active"] = self.active if self.display_name is not None: body["displayName"] = self.display_name if self.emails: body["emails"] = self.emails if self.external_id is not None: body["externalId"] = self.external_id if self.id is not None: body["id"] = self.id if self.name: body["name"] = self.name if self.roles: body["roles"] = self.roles if self.user_name is not None: body["userName"] = self.user_name return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> AccountUser: """Deserializes the AccountUser from a dictionary.""" return cls( account_id=d.get("account_id", None), active=d.get("active", None), display_name=d.get("displayName", None), emails=_repeated_dict(d, "emails", ComplexValue), external_id=d.get("externalId", None), id=d.get("id", None), name=_from_dict(d, "name", Name), roles=_repeated_dict(d, "roles", ComplexValue), user_name=d.get("userName", None), ) @dataclass class Actor: """represents an identity trying to access a resource - user or a service principal group can be a principal of a permission set assignment but an actor is always a user or a service principal""" actor_id: Optional[int] = None def as_dict(self) -> dict: """Serializes the Actor into a dictionary suitable for use as a JSON request body.""" body = {} if self.actor_id is not None: body["actor_id"] = self.actor_id return body def as_shallow_dict(self) -> dict: """Serializes the Actor into a shallow dictionary of its immediate attributes.""" body = {} if self.actor_id is not None: body["actor_id"] = self.actor_id return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> Actor: """Deserializes the Actor from a dictionary.""" return cls(actor_id=d.get("actor_id", None)) @dataclass class CheckPolicyResponse: consistency_token: ConsistencyToken is_permitted: Optional[bool] = None def as_dict(self) -> dict: """Serializes the CheckPolicyResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.consistency_token: body["consistency_token"] = self.consistency_token.as_dict() if self.is_permitted is not None: body["is_permitted"] = self.is_permitted return body def as_shallow_dict(self) -> dict: """Serializes the CheckPolicyResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.consistency_token: body["consistency_token"] = self.consistency_token if self.is_permitted is not None: body["is_permitted"] = self.is_permitted return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> CheckPolicyResponse: """Deserializes the CheckPolicyResponse from a dictionary.""" return cls( consistency_token=_from_dict(d, "consistency_token", ConsistencyToken), is_permitted=d.get("is_permitted", None), ) @dataclass class ComplexValue: display: Optional[str] = None primary: Optional[bool] = None ref: Optional[str] = None type: Optional[str] = None value: Optional[str] = None def as_dict(self) -> dict: """Serializes the ComplexValue into a dictionary suitable for use as a JSON request body.""" body = {} if self.display is not None: body["display"] = self.display if self.primary is not None: body["primary"] = self.primary if self.ref is not None: body["$ref"] = self.ref if self.type is not None: body["type"] = self.type if self.value is not None: body["value"] = self.value return body def as_shallow_dict(self) -> dict: """Serializes the ComplexValue into a shallow dictionary of its immediate attributes.""" body = {} if self.display is not None: body["display"] = self.display if self.primary is not None: body["primary"] = self.primary if self.ref is not None: body["$ref"] = self.ref if self.type is not None: body["type"] = self.type if self.value is not None: body["value"] = self.value return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> ComplexValue: """Deserializes the ComplexValue from a dictionary.""" return cls( display=d.get("display", None), primary=d.get("primary", None), ref=d.get("$ref", None), type=d.get("type", None), value=d.get("value", None), ) @dataclass class ConsistencyToken: value: str def as_dict(self) -> dict: """Serializes the ConsistencyToken into a dictionary suitable for use as a JSON request body.""" body = {} if self.value is not None: body["value"] = self.value return body def as_shallow_dict(self) -> dict: """Serializes the ConsistencyToken into a shallow dictionary of its immediate attributes.""" body = {} if self.value is not None: body["value"] = self.value return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> ConsistencyToken: """Deserializes the ConsistencyToken from a dictionary.""" return cls(value=d.get("value", None)) @dataclass class DeleteWorkspacePermissionAssignmentResponse: def as_dict(self) -> dict: """Serializes the DeleteWorkspacePermissionAssignmentResponse into a dictionary suitable for use as a JSON request body.""" body = {} return body def as_shallow_dict(self) -> dict: """Serializes the DeleteWorkspacePermissionAssignmentResponse into a shallow dictionary of its immediate attributes.""" body = {} return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> DeleteWorkspacePermissionAssignmentResponse: """Deserializes the DeleteWorkspacePermissionAssignmentResponse from a dictionary.""" return cls() @dataclass class GetAssignableRolesForResourceResponse: roles: Optional[List[Role]] = None def as_dict(self) -> dict: """Serializes the GetAssignableRolesForResourceResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.roles: body["roles"] = [v.as_dict() for v in self.roles] return body def as_shallow_dict(self) -> dict: """Serializes the GetAssignableRolesForResourceResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.roles: body["roles"] = self.roles return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> GetAssignableRolesForResourceResponse: """Deserializes the GetAssignableRolesForResourceResponse from a dictionary.""" return cls(roles=_repeated_dict(d, "roles", Role)) @dataclass class GetPasswordPermissionLevelsResponse: permission_levels: Optional[List[PasswordPermissionsDescription]] = None """Specific permission levels""" def as_dict(self) -> dict: """Serializes the GetPasswordPermissionLevelsResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.permission_levels: body["permission_levels"] = [v.as_dict() for v in self.permission_levels] return body def as_shallow_dict(self) -> dict: """Serializes the GetPasswordPermissionLevelsResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.permission_levels: body["permission_levels"] = self.permission_levels return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> GetPasswordPermissionLevelsResponse: """Deserializes the GetPasswordPermissionLevelsResponse from a dictionary.""" return cls(permission_levels=_repeated_dict(d, "permission_levels", PasswordPermissionsDescription)) @dataclass class GetPermissionLevelsResponse: permission_levels: Optional[List[PermissionsDescription]] = None """Specific permission levels""" def as_dict(self) -> dict: """Serializes the GetPermissionLevelsResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.permission_levels: body["permission_levels"] = [v.as_dict() for v in self.permission_levels] return body def as_shallow_dict(self) -> dict: """Serializes the GetPermissionLevelsResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.permission_levels: body["permission_levels"] = self.permission_levels return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> GetPermissionLevelsResponse: """Deserializes the GetPermissionLevelsResponse from a dictionary.""" return cls(permission_levels=_repeated_dict(d, "permission_levels", PermissionsDescription)) class GetSortOrder(Enum): ASCENDING = "ascending" DESCENDING = "descending" @dataclass class GrantRule: role: str """Role that is assigned to the list of principals.""" principals: Optional[List[str]] = None """Principals this grant rule applies to. A principal can be a user (for end users), a service principal (for applications and compute workloads), or an account group. Each principal has its own identifier format: * users/ * groups/ * servicePrincipals/""" def as_dict(self) -> dict: """Serializes the GrantRule into a dictionary suitable for use as a JSON request body.""" body = {} if self.principals: body["principals"] = [v for v in self.principals] if self.role is not None: body["role"] = self.role return body def as_shallow_dict(self) -> dict: """Serializes the GrantRule into a shallow dictionary of its immediate attributes.""" body = {} if self.principals: body["principals"] = self.principals if self.role is not None: body["role"] = self.role return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> GrantRule: """Deserializes the GrantRule from a dictionary.""" return cls(principals=d.get("principals", None), role=d.get("role", None)) @dataclass class Group: display_name: Optional[str] = None """String that represents a human-readable group name""" entitlements: Optional[List[ComplexValue]] = None """Entitlements assigned to the group. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements""" external_id: Optional[str] = None """external_id should be unique for identifying groups""" groups: Optional[List[ComplexValue]] = None id: Optional[str] = None """Databricks group ID""" members: Optional[List[ComplexValue]] = None meta: Optional[ResourceMeta] = None """Container for the group identifier. Workspace local versus account.""" roles: Optional[List[ComplexValue]] = None """Corresponds to AWS instance profile/arn role.""" schemas: Optional[List[GroupSchema]] = None """The schema of the group.""" def as_dict(self) -> dict: """Serializes the Group into a dictionary suitable for use as a JSON request body.""" body = {} if self.display_name is not None: body["displayName"] = self.display_name if self.entitlements: body["entitlements"] = [v.as_dict() for v in self.entitlements] if self.external_id is not None: body["externalId"] = self.external_id if self.groups: body["groups"] = [v.as_dict() for v in self.groups] if self.id is not None: body["id"] = self.id if self.members: body["members"] = [v.as_dict() for v in self.members] if self.meta: body["meta"] = self.meta.as_dict() if self.roles: body["roles"] = [v.as_dict() for v in self.roles] if self.schemas: body["schemas"] = [v.value for v in self.schemas] return body def as_shallow_dict(self) -> dict: """Serializes the Group into a shallow dictionary of its immediate attributes.""" body = {} if self.display_name is not None: body["displayName"] = self.display_name if self.entitlements: body["entitlements"] = self.entitlements if self.external_id is not None: body["externalId"] = self.external_id if self.groups: body["groups"] = self.groups if self.id is not None: body["id"] = self.id if self.members: body["members"] = self.members if self.meta: body["meta"] = self.meta if self.roles: body["roles"] = self.roles if self.schemas: body["schemas"] = self.schemas return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> Group: """Deserializes the Group from a dictionary.""" return cls( display_name=d.get("displayName", None), entitlements=_repeated_dict(d, "entitlements", ComplexValue), external_id=d.get("externalId", None), groups=_repeated_dict(d, "groups", ComplexValue), id=d.get("id", None), members=_repeated_dict(d, "members", ComplexValue), meta=_from_dict(d, "meta", ResourceMeta), roles=_repeated_dict(d, "roles", ComplexValue), schemas=_repeated_enum(d, "schemas", GroupSchema), ) class GroupSchema(Enum): URN_IETF_PARAMS_SCIM_SCHEMAS_CORE_2_0_GROUP = "urn:ietf:params:scim:schemas:core:2.0:Group" @dataclass class ListAccountGroupsResponse: items_per_page: Optional[int] = None """Total results returned in the response.""" resources: Optional[List[AccountGroup]] = None """User objects returned in the response.""" start_index: Optional[int] = None """Starting index of all the results that matched the request filters. First item is number 1.""" total_results: Optional[int] = None """Total results that match the request filters.""" def as_dict(self) -> dict: """Serializes the ListAccountGroupsResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.items_per_page is not None: body["itemsPerPage"] = self.items_per_page if self.resources: body["Resources"] = [v.as_dict() for v in self.resources] if self.start_index is not None: body["startIndex"] = self.start_index if self.total_results is not None: body["totalResults"] = self.total_results return body def as_shallow_dict(self) -> dict: """Serializes the ListAccountGroupsResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.items_per_page is not None: body["itemsPerPage"] = self.items_per_page if self.resources: body["Resources"] = self.resources if self.start_index is not None: body["startIndex"] = self.start_index if self.total_results is not None: body["totalResults"] = self.total_results return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> ListAccountGroupsResponse: """Deserializes the ListAccountGroupsResponse from a dictionary.""" return cls( items_per_page=d.get("itemsPerPage", None), resources=_repeated_dict(d, "Resources", AccountGroup), start_index=d.get("startIndex", None), total_results=d.get("totalResults", None), ) @dataclass class ListAccountServicePrincipalsResponse: items_per_page: Optional[int] = None """Total results returned in the response.""" resources: Optional[List[AccountServicePrincipal]] = None """User objects returned in the response.""" start_index: Optional[int] = None """Starting index of all the results that matched the request filters. First item is number 1.""" total_results: Optional[int] = None """Total results that match the request filters.""" def as_dict(self) -> dict: """Serializes the ListAccountServicePrincipalsResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.items_per_page is not None: body["itemsPerPage"] = self.items_per_page if self.resources: body["Resources"] = [v.as_dict() for v in self.resources] if self.start_index is not None: body["startIndex"] = self.start_index if self.total_results is not None: body["totalResults"] = self.total_results return body def as_shallow_dict(self) -> dict: """Serializes the ListAccountServicePrincipalsResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.items_per_page is not None: body["itemsPerPage"] = self.items_per_page if self.resources: body["Resources"] = self.resources if self.start_index is not None: body["startIndex"] = self.start_index if self.total_results is not None: body["totalResults"] = self.total_results return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> ListAccountServicePrincipalsResponse: """Deserializes the ListAccountServicePrincipalsResponse from a dictionary.""" return cls( items_per_page=d.get("itemsPerPage", None), resources=_repeated_dict(d, "Resources", AccountServicePrincipal), start_index=d.get("startIndex", None), total_results=d.get("totalResults", None), ) @dataclass class ListAccountUsersResponse: items_per_page: Optional[int] = None """Total results returned in the response.""" resources: Optional[List[AccountUser]] = None """User objects returned in the response.""" start_index: Optional[int] = None """Starting index of all the results that matched the request filters. First item is number 1.""" total_results: Optional[int] = None """Total results that match the request filters.""" def as_dict(self) -> dict: """Serializes the ListAccountUsersResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.items_per_page is not None: body["itemsPerPage"] = self.items_per_page if self.resources: body["Resources"] = [v.as_dict() for v in self.resources] if self.start_index is not None: body["startIndex"] = self.start_index if self.total_results is not None: body["totalResults"] = self.total_results return body def as_shallow_dict(self) -> dict: """Serializes the ListAccountUsersResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.items_per_page is not None: body["itemsPerPage"] = self.items_per_page if self.resources: body["Resources"] = self.resources if self.start_index is not None: body["startIndex"] = self.start_index if self.total_results is not None: body["totalResults"] = self.total_results return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> ListAccountUsersResponse: """Deserializes the ListAccountUsersResponse from a dictionary.""" return cls( items_per_page=d.get("itemsPerPage", None), resources=_repeated_dict(d, "Resources", AccountUser), start_index=d.get("startIndex", None), total_results=d.get("totalResults", None), ) @dataclass class ListGroupsResponse: items_per_page: Optional[int] = None """Total results returned in the response.""" resources: Optional[List[Group]] = None """User objects returned in the response.""" schemas: Optional[List[ListResponseSchema]] = None """The schema of the service principal.""" start_index: Optional[int] = None """Starting index of all the results that matched the request filters. First item is number 1.""" total_results: Optional[int] = None """Total results that match the request filters.""" def as_dict(self) -> dict: """Serializes the ListGroupsResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.items_per_page is not None: body["itemsPerPage"] = self.items_per_page if self.resources: body["Resources"] = [v.as_dict() for v in self.resources] if self.schemas: body["schemas"] = [v.value for v in self.schemas] if self.start_index is not None: body["startIndex"] = self.start_index if self.total_results is not None: body["totalResults"] = self.total_results return body def as_shallow_dict(self) -> dict: """Serializes the ListGroupsResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.items_per_page is not None: body["itemsPerPage"] = self.items_per_page if self.resources: body["Resources"] = self.resources if self.schemas: body["schemas"] = self.schemas if self.start_index is not None: body["startIndex"] = self.start_index if self.total_results is not None: body["totalResults"] = self.total_results return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> ListGroupsResponse: """Deserializes the ListGroupsResponse from a dictionary.""" return cls( items_per_page=d.get("itemsPerPage", None), resources=_repeated_dict(d, "Resources", Group), schemas=_repeated_enum(d, "schemas", ListResponseSchema), start_index=d.get("startIndex", None), total_results=d.get("totalResults", None), ) class ListResponseSchema(Enum): URN_IETF_PARAMS_SCIM_API_MESSAGES_2_0_LIST_RESPONSE = "urn:ietf:params:scim:api:messages:2.0:ListResponse" @dataclass class ListServicePrincipalResponse: items_per_page: Optional[int] = None """Total results returned in the response.""" resources: Optional[List[ServicePrincipal]] = None """User objects returned in the response.""" schemas: Optional[List[ListResponseSchema]] = None """The schema of the List response.""" start_index: Optional[int] = None """Starting index of all the results that matched the request filters. First item is number 1.""" total_results: Optional[int] = None """Total results that match the request filters.""" def as_dict(self) -> dict: """Serializes the ListServicePrincipalResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.items_per_page is not None: body["itemsPerPage"] = self.items_per_page if self.resources: body["Resources"] = [v.as_dict() for v in self.resources] if self.schemas: body["schemas"] = [v.value for v in self.schemas] if self.start_index is not None: body["startIndex"] = self.start_index if self.total_results is not None: body["totalResults"] = self.total_results return body def as_shallow_dict(self) -> dict: """Serializes the ListServicePrincipalResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.items_per_page is not None: body["itemsPerPage"] = self.items_per_page if self.resources: body["Resources"] = self.resources if self.schemas: body["schemas"] = self.schemas if self.start_index is not None: body["startIndex"] = self.start_index if self.total_results is not None: body["totalResults"] = self.total_results return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> ListServicePrincipalResponse: """Deserializes the ListServicePrincipalResponse from a dictionary.""" return cls( items_per_page=d.get("itemsPerPage", None), resources=_repeated_dict(d, "Resources", ServicePrincipal), schemas=_repeated_enum(d, "schemas", ListResponseSchema), start_index=d.get("startIndex", None), total_results=d.get("totalResults", None), ) class ListSortOrder(Enum): ASCENDING = "ascending" DESCENDING = "descending" @dataclass class ListUsersResponse: items_per_page: Optional[int] = None """Total results returned in the response.""" resources: Optional[List[User]] = None """User objects returned in the response.""" schemas: Optional[List[ListResponseSchema]] = None """The schema of the List response.""" start_index: Optional[int] = None """Starting index of all the results that matched the request filters. First item is number 1.""" total_results: Optional[int] = None """Total results that match the request filters.""" def as_dict(self) -> dict: """Serializes the ListUsersResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.items_per_page is not None: body["itemsPerPage"] = self.items_per_page if self.resources: body["Resources"] = [v.as_dict() for v in self.resources] if self.schemas: body["schemas"] = [v.value for v in self.schemas] if self.start_index is not None: body["startIndex"] = self.start_index if self.total_results is not None: body["totalResults"] = self.total_results return body def as_shallow_dict(self) -> dict: """Serializes the ListUsersResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.items_per_page is not None: body["itemsPerPage"] = self.items_per_page if self.resources: body["Resources"] = self.resources if self.schemas: body["schemas"] = self.schemas if self.start_index is not None: body["startIndex"] = self.start_index if self.total_results is not None: body["totalResults"] = self.total_results return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> ListUsersResponse: """Deserializes the ListUsersResponse from a dictionary.""" return cls( items_per_page=d.get("itemsPerPage", None), resources=_repeated_dict(d, "Resources", User), schemas=_repeated_enum(d, "schemas", ListResponseSchema), start_index=d.get("startIndex", None), total_results=d.get("totalResults", None), ) @dataclass class MigratePermissionsResponse: permissions_migrated: Optional[int] = None """Number of permissions migrated.""" def as_dict(self) -> dict: """Serializes the MigratePermissionsResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.permissions_migrated is not None: body["permissions_migrated"] = self.permissions_migrated return body def as_shallow_dict(self) -> dict: """Serializes the MigratePermissionsResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.permissions_migrated is not None: body["permissions_migrated"] = self.permissions_migrated return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> MigratePermissionsResponse: """Deserializes the MigratePermissionsResponse from a dictionary.""" return cls(permissions_migrated=d.get("permissions_migrated", None)) @dataclass class Name: family_name: Optional[str] = None """Family name of the Databricks user.""" given_name: Optional[str] = None """Given name of the Databricks user.""" def as_dict(self) -> dict: """Serializes the Name into a dictionary suitable for use as a JSON request body.""" body = {} if self.family_name is not None: body["familyName"] = self.family_name if self.given_name is not None: body["givenName"] = self.given_name return body def as_shallow_dict(self) -> dict: """Serializes the Name into a shallow dictionary of its immediate attributes.""" body = {} if self.family_name is not None: body["familyName"] = self.family_name if self.given_name is not None: body["givenName"] = self.given_name return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> Name: """Deserializes the Name from a dictionary.""" return cls(family_name=d.get("familyName", None), given_name=d.get("givenName", None)) @dataclass class ObjectPermissions: access_control_list: Optional[List[AccessControlResponse]] = None object_id: Optional[str] = None object_type: Optional[str] = None def as_dict(self) -> dict: """Serializes the ObjectPermissions into a dictionary suitable for use as a JSON request body.""" body = {} if self.access_control_list: body["access_control_list"] = [v.as_dict() for v in self.access_control_list] if self.object_id is not None: body["object_id"] = self.object_id if self.object_type is not None: body["object_type"] = self.object_type return body def as_shallow_dict(self) -> dict: """Serializes the ObjectPermissions into a shallow dictionary of its immediate attributes.""" body = {} if self.access_control_list: body["access_control_list"] = self.access_control_list if self.object_id is not None: body["object_id"] = self.object_id if self.object_type is not None: body["object_type"] = self.object_type return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> ObjectPermissions: """Deserializes the ObjectPermissions from a dictionary.""" return cls( access_control_list=_repeated_dict(d, "access_control_list", AccessControlResponse), object_id=d.get("object_id", None), object_type=d.get("object_type", None), ) @dataclass class PasswordAccessControlRequest: group_name: Optional[str] = None """name of the group""" permission_level: Optional[PasswordPermissionLevel] = None """Permission level""" service_principal_name: Optional[str] = None """application ID of a service principal""" user_name: Optional[str] = None """name of the user""" def as_dict(self) -> dict: """Serializes the PasswordAccessControlRequest into a dictionary suitable for use as a JSON request body.""" body = {} if self.group_name is not None: body["group_name"] = self.group_name if self.permission_level is not None: body["permission_level"] = self.permission_level.value if self.service_principal_name is not None: body["service_principal_name"] = self.service_principal_name if self.user_name is not None: body["user_name"] = self.user_name return body def as_shallow_dict(self) -> dict: """Serializes the PasswordAccessControlRequest into a shallow dictionary of its immediate attributes.""" body = {} if self.group_name is not None: body["group_name"] = self.group_name if self.permission_level is not None: body["permission_level"] = self.permission_level if self.service_principal_name is not None: body["service_principal_name"] = self.service_principal_name if self.user_name is not None: body["user_name"] = self.user_name return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> PasswordAccessControlRequest: """Deserializes the PasswordAccessControlRequest from a dictionary.""" return cls( group_name=d.get("group_name", None), permission_level=_enum(d, "permission_level", PasswordPermissionLevel), service_principal_name=d.get("service_principal_name", None), user_name=d.get("user_name", None), ) @dataclass class PasswordAccessControlResponse: all_permissions: Optional[List[PasswordPermission]] = None """All permissions.""" display_name: Optional[str] = None """Display name of the user or service principal.""" group_name: Optional[str] = None """name of the group""" service_principal_name: Optional[str] = None """Name of the service principal.""" user_name: Optional[str] = None """name of the user""" def as_dict(self) -> dict: """Serializes the PasswordAccessControlResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.all_permissions: body["all_permissions"] = [v.as_dict() for v in self.all_permissions] if self.display_name is not None: body["display_name"] = self.display_name if self.group_name is not None: body["group_name"] = self.group_name if self.service_principal_name is not None: body["service_principal_name"] = self.service_principal_name if self.user_name is not None: body["user_name"] = self.user_name return body def as_shallow_dict(self) -> dict: """Serializes the PasswordAccessControlResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.all_permissions: body["all_permissions"] = self.all_permissions if self.display_name is not None: body["display_name"] = self.display_name if self.group_name is not None: body["group_name"] = self.group_name if self.service_principal_name is not None: body["service_principal_name"] = self.service_principal_name if self.user_name is not None: body["user_name"] = self.user_name return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> PasswordAccessControlResponse: """Deserializes the PasswordAccessControlResponse from a dictionary.""" return cls( all_permissions=_repeated_dict(d, "all_permissions", PasswordPermission), display_name=d.get("display_name", None), group_name=d.get("group_name", None), service_principal_name=d.get("service_principal_name", None), user_name=d.get("user_name", None), ) @dataclass class PasswordPermission: inherited: Optional[bool] = None inherited_from_object: Optional[List[str]] = None permission_level: Optional[PasswordPermissionLevel] = None """Permission level""" def as_dict(self) -> dict: """Serializes the PasswordPermission into a dictionary suitable for use as a JSON request body.""" body = {} if self.inherited is not None: body["inherited"] = self.inherited if self.inherited_from_object: body["inherited_from_object"] = [v for v in self.inherited_from_object] if self.permission_level is not None: body["permission_level"] = self.permission_level.value return body def as_shallow_dict(self) -> dict: """Serializes the PasswordPermission into a shallow dictionary of its immediate attributes.""" body = {} if self.inherited is not None: body["inherited"] = self.inherited if self.inherited_from_object: body["inherited_from_object"] = self.inherited_from_object if self.permission_level is not None: body["permission_level"] = self.permission_level return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> PasswordPermission: """Deserializes the PasswordPermission from a dictionary.""" return cls( inherited=d.get("inherited", None), inherited_from_object=d.get("inherited_from_object", None), permission_level=_enum(d, "permission_level", PasswordPermissionLevel), ) class PasswordPermissionLevel(Enum): """Permission level""" CAN_USE = "CAN_USE" @dataclass class PasswordPermissions: access_control_list: Optional[List[PasswordAccessControlResponse]] = None object_id: Optional[str] = None object_type: Optional[str] = None def as_dict(self) -> dict: """Serializes the PasswordPermissions into a dictionary suitable for use as a JSON request body.""" body = {} if self.access_control_list: body["access_control_list"] = [v.as_dict() for v in self.access_control_list] if self.object_id is not None: body["object_id"] = self.object_id if self.object_type is not None: body["object_type"] = self.object_type return body def as_shallow_dict(self) -> dict: """Serializes the PasswordPermissions into a shallow dictionary of its immediate attributes.""" body = {} if self.access_control_list: body["access_control_list"] = self.access_control_list if self.object_id is not None: body["object_id"] = self.object_id if self.object_type is not None: body["object_type"] = self.object_type return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> PasswordPermissions: """Deserializes the PasswordPermissions from a dictionary.""" return cls( access_control_list=_repeated_dict(d, "access_control_list", PasswordAccessControlResponse), object_id=d.get("object_id", None), object_type=d.get("object_type", None), ) @dataclass class PasswordPermissionsDescription: description: Optional[str] = None permission_level: Optional[PasswordPermissionLevel] = None """Permission level""" def as_dict(self) -> dict: """Serializes the PasswordPermissionsDescription into a dictionary suitable for use as a JSON request body.""" body = {} if self.description is not None: body["description"] = self.description if self.permission_level is not None: body["permission_level"] = self.permission_level.value return body def as_shallow_dict(self) -> dict: """Serializes the PasswordPermissionsDescription into a shallow dictionary of its immediate attributes.""" body = {} if self.description is not None: body["description"] = self.description if self.permission_level is not None: body["permission_level"] = self.permission_level return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> PasswordPermissionsDescription: """Deserializes the PasswordPermissionsDescription from a dictionary.""" return cls( description=d.get("description", None), permission_level=_enum(d, "permission_level", PasswordPermissionLevel), ) @dataclass class Patch: op: Optional[PatchOp] = None """Type of patch operation.""" path: Optional[str] = None """Selection of patch operation""" value: Optional[Any] = None """Value to modify""" def as_dict(self) -> dict: """Serializes the Patch into a dictionary suitable for use as a JSON request body.""" body = {} if self.op is not None: body["op"] = self.op.value if self.path is not None: body["path"] = self.path if self.value: body["value"] = self.value return body def as_shallow_dict(self) -> dict: """Serializes the Patch into a shallow dictionary of its immediate attributes.""" body = {} if self.op is not None: body["op"] = self.op if self.path is not None: body["path"] = self.path if self.value: body["value"] = self.value return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> Patch: """Deserializes the Patch from a dictionary.""" return cls(op=_enum(d, "op", PatchOp), path=d.get("path", None), value=d.get("value", None)) class PatchOp(Enum): """Type of patch operation.""" ADD = "add" REMOVE = "remove" REPLACE = "replace" class PatchSchema(Enum): URN_IETF_PARAMS_SCIM_API_MESSAGES_2_0_PATCH_OP = "urn:ietf:params:scim:api:messages:2.0:PatchOp" @dataclass class Permission: inherited: Optional[bool] = None inherited_from_object: Optional[List[str]] = None permission_level: Optional[PermissionLevel] = None def as_dict(self) -> dict: """Serializes the Permission into a dictionary suitable for use as a JSON request body.""" body = {} if self.inherited is not None: body["inherited"] = self.inherited if self.inherited_from_object: body["inherited_from_object"] = [v for v in self.inherited_from_object] if self.permission_level is not None: body["permission_level"] = self.permission_level.value return body def as_shallow_dict(self) -> dict: """Serializes the Permission into a shallow dictionary of its immediate attributes.""" body = {} if self.inherited is not None: body["inherited"] = self.inherited if self.inherited_from_object: body["inherited_from_object"] = self.inherited_from_object if self.permission_level is not None: body["permission_level"] = self.permission_level return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> Permission: """Deserializes the Permission from a dictionary.""" return cls( inherited=d.get("inherited", None), inherited_from_object=d.get("inherited_from_object", None), permission_level=_enum(d, "permission_level", PermissionLevel), ) @dataclass class PermissionAssignment: """The output format for existing workspace PermissionAssignment records, which contains some info for user consumption.""" error: Optional[str] = None """Error response associated with a workspace permission assignment, if any.""" permissions: Optional[List[WorkspacePermission]] = None """The permissions level of the principal.""" principal: Optional[PrincipalOutput] = None """Information about the principal assigned to the workspace.""" def as_dict(self) -> dict: """Serializes the PermissionAssignment into a dictionary suitable for use as a JSON request body.""" body = {} if self.error is not None: body["error"] = self.error if self.permissions: body["permissions"] = [v.value for v in self.permissions] if self.principal: body["principal"] = self.principal.as_dict() return body def as_shallow_dict(self) -> dict: """Serializes the PermissionAssignment into a shallow dictionary of its immediate attributes.""" body = {} if self.error is not None: body["error"] = self.error if self.permissions: body["permissions"] = self.permissions if self.principal: body["principal"] = self.principal return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> PermissionAssignment: """Deserializes the PermissionAssignment from a dictionary.""" return cls( error=d.get("error", None), permissions=_repeated_enum(d, "permissions", WorkspacePermission), principal=_from_dict(d, "principal", PrincipalOutput), ) @dataclass class PermissionAssignments: permission_assignments: Optional[List[PermissionAssignment]] = None """Array of permissions assignments defined for a workspace.""" def as_dict(self) -> dict: """Serializes the PermissionAssignments into a dictionary suitable for use as a JSON request body.""" body = {} if self.permission_assignments: body["permission_assignments"] = [v.as_dict() for v in self.permission_assignments] return body def as_shallow_dict(self) -> dict: """Serializes the PermissionAssignments into a shallow dictionary of its immediate attributes.""" body = {} if self.permission_assignments: body["permission_assignments"] = self.permission_assignments return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> PermissionAssignments: """Deserializes the PermissionAssignments from a dictionary.""" return cls(permission_assignments=_repeated_dict(d, "permission_assignments", PermissionAssignment)) class PermissionLevel(Enum): """Permission level""" CAN_ATTACH_TO = "CAN_ATTACH_TO" CAN_BIND = "CAN_BIND" CAN_CREATE = "CAN_CREATE" CAN_EDIT = "CAN_EDIT" CAN_EDIT_METADATA = "CAN_EDIT_METADATA" CAN_MANAGE = "CAN_MANAGE" CAN_MANAGE_PRODUCTION_VERSIONS = "CAN_MANAGE_PRODUCTION_VERSIONS" CAN_MANAGE_RUN = "CAN_MANAGE_RUN" CAN_MANAGE_STAGING_VERSIONS = "CAN_MANAGE_STAGING_VERSIONS" CAN_MONITOR = "CAN_MONITOR" CAN_MONITOR_ONLY = "CAN_MONITOR_ONLY" CAN_QUERY = "CAN_QUERY" CAN_READ = "CAN_READ" CAN_RESTART = "CAN_RESTART" CAN_RUN = "CAN_RUN" CAN_USE = "CAN_USE" CAN_VIEW = "CAN_VIEW" CAN_VIEW_METADATA = "CAN_VIEW_METADATA" IS_OWNER = "IS_OWNER" @dataclass class PermissionOutput: description: Optional[str] = None """The results of a permissions query.""" permission_level: Optional[WorkspacePermission] = None def as_dict(self) -> dict: """Serializes the PermissionOutput into a dictionary suitable for use as a JSON request body.""" body = {} if self.description is not None: body["description"] = self.description if self.permission_level is not None: body["permission_level"] = self.permission_level.value return body def as_shallow_dict(self) -> dict: """Serializes the PermissionOutput into a shallow dictionary of its immediate attributes.""" body = {} if self.description is not None: body["description"] = self.description if self.permission_level is not None: body["permission_level"] = self.permission_level return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> PermissionOutput: """Deserializes the PermissionOutput from a dictionary.""" return cls( description=d.get("description", None), permission_level=_enum(d, "permission_level", WorkspacePermission) ) @dataclass class PermissionsDescription: description: Optional[str] = None permission_level: Optional[PermissionLevel] = None def as_dict(self) -> dict: """Serializes the PermissionsDescription into a dictionary suitable for use as a JSON request body.""" body = {} if self.description is not None: body["description"] = self.description if self.permission_level is not None: body["permission_level"] = self.permission_level.value return body def as_shallow_dict(self) -> dict: """Serializes the PermissionsDescription into a shallow dictionary of its immediate attributes.""" body = {} if self.description is not None: body["description"] = self.description if self.permission_level is not None: body["permission_level"] = self.permission_level return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> PermissionsDescription: """Deserializes the PermissionsDescription from a dictionary.""" return cls( description=d.get("description", None), permission_level=_enum(d, "permission_level", PermissionLevel) ) @dataclass class PrincipalOutput: """Information about the principal assigned to the workspace.""" display_name: Optional[str] = None """The display name of the principal.""" group_name: Optional[str] = None """The group name of the group. Present only if the principal is a group.""" principal_id: Optional[int] = None """The unique, opaque id of the principal.""" service_principal_name: Optional[str] = None """The name of the service principal. Present only if the principal is a service principal.""" user_name: Optional[str] = None """The username of the user. Present only if the principal is a user.""" def as_dict(self) -> dict: """Serializes the PrincipalOutput into a dictionary suitable for use as a JSON request body.""" body = {} if self.display_name is not None: body["display_name"] = self.display_name if self.group_name is not None: body["group_name"] = self.group_name if self.principal_id is not None: body["principal_id"] = self.principal_id if self.service_principal_name is not None: body["service_principal_name"] = self.service_principal_name if self.user_name is not None: body["user_name"] = self.user_name return body def as_shallow_dict(self) -> dict: """Serializes the PrincipalOutput into a shallow dictionary of its immediate attributes.""" body = {} if self.display_name is not None: body["display_name"] = self.display_name if self.group_name is not None: body["group_name"] = self.group_name if self.principal_id is not None: body["principal_id"] = self.principal_id if self.service_principal_name is not None: body["service_principal_name"] = self.service_principal_name if self.user_name is not None: body["user_name"] = self.user_name return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> PrincipalOutput: """Deserializes the PrincipalOutput from a dictionary.""" return cls( display_name=d.get("display_name", None), group_name=d.get("group_name", None), principal_id=d.get("principal_id", None), service_principal_name=d.get("service_principal_name", None), user_name=d.get("user_name", None), ) class RequestAuthzIdentity(Enum): """Defines the identity to be used for authZ of the request on the server side. See one pager for for more information: http://go/acl/service-identity""" REQUEST_AUTHZ_IDENTITY_SERVICE_IDENTITY = "REQUEST_AUTHZ_IDENTITY_SERVICE_IDENTITY" REQUEST_AUTHZ_IDENTITY_USER_CONTEXT = "REQUEST_AUTHZ_IDENTITY_USER_CONTEXT" @dataclass class ResourceInfo: id: str """Id of the current resource.""" legacy_acl_path: Optional[str] = None """The legacy acl path of the current resource.""" parent_resource_info: Optional[ResourceInfo] = None """Parent resource info for the current resource. The parent may have another parent.""" def as_dict(self) -> dict: """Serializes the ResourceInfo into a dictionary suitable for use as a JSON request body.""" body = {} if self.id is not None: body["id"] = self.id if self.legacy_acl_path is not None: body["legacy_acl_path"] = self.legacy_acl_path if self.parent_resource_info: body["parent_resource_info"] = self.parent_resource_info.as_dict() return body def as_shallow_dict(self) -> dict: """Serializes the ResourceInfo into a shallow dictionary of its immediate attributes.""" body = {} if self.id is not None: body["id"] = self.id if self.legacy_acl_path is not None: body["legacy_acl_path"] = self.legacy_acl_path if self.parent_resource_info: body["parent_resource_info"] = self.parent_resource_info return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> ResourceInfo: """Deserializes the ResourceInfo from a dictionary.""" return cls( id=d.get("id", None), legacy_acl_path=d.get("legacy_acl_path", None), parent_resource_info=_from_dict(d, "parent_resource_info", ResourceInfo), ) @dataclass class ResourceMeta: resource_type: Optional[str] = None """Identifier for group type. Can be local workspace group (`WorkspaceGroup`) or account group (`Group`).""" def as_dict(self) -> dict: """Serializes the ResourceMeta into a dictionary suitable for use as a JSON request body.""" body = {} if self.resource_type is not None: body["resourceType"] = self.resource_type return body def as_shallow_dict(self) -> dict: """Serializes the ResourceMeta into a shallow dictionary of its immediate attributes.""" body = {} if self.resource_type is not None: body["resourceType"] = self.resource_type return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> ResourceMeta: """Deserializes the ResourceMeta from a dictionary.""" return cls(resource_type=d.get("resourceType", None)) @dataclass class Role: name: str """Role to assign to a principal or a list of principals on a resource.""" def as_dict(self) -> dict: """Serializes the Role into a dictionary suitable for use as a JSON request body.""" body = {} if self.name is not None: body["name"] = self.name return body def as_shallow_dict(self) -> dict: """Serializes the Role into a shallow dictionary of its immediate attributes.""" body = {} if self.name is not None: body["name"] = self.name return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> Role: """Deserializes the Role from a dictionary.""" return cls(name=d.get("name", None)) @dataclass class RuleSetResponse: name: str """Name of the rule set.""" etag: str """Identifies the version of the rule set returned. Etag used for versioning. The response is at least as fresh as the eTag provided. Etag is used for optimistic concurrency control as a way to help prevent simultaneous updates of a rule set from overwriting each other. It is strongly suggested that systems make use of the etag in the read -> modify -> write pattern to perform rule set updates in order to avoid race conditions that is get an etag from a GET rule set request, and pass it with the PUT update request to identify the rule set version you are updating.""" grant_rules: Optional[List[GrantRule]] = None def as_dict(self) -> dict: """Serializes the RuleSetResponse into a dictionary suitable for use as a JSON request body.""" body = {} if self.etag is not None: body["etag"] = self.etag if self.grant_rules: body["grant_rules"] = [v.as_dict() for v in self.grant_rules] if self.name is not None: body["name"] = self.name return body def as_shallow_dict(self) -> dict: """Serializes the RuleSetResponse into a shallow dictionary of its immediate attributes.""" body = {} if self.etag is not None: body["etag"] = self.etag if self.grant_rules: body["grant_rules"] = self.grant_rules if self.name is not None: body["name"] = self.name return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> RuleSetResponse: """Deserializes the RuleSetResponse from a dictionary.""" return cls( etag=d.get("etag", None), grant_rules=_repeated_dict(d, "grant_rules", GrantRule), name=d.get("name", None) ) @dataclass class RuleSetUpdateRequest: name: str """Name of the rule set.""" etag: str """Identifies the version of the rule set returned. Etag used for versioning. The response is at least as fresh as the eTag provided. Etag is used for optimistic concurrency control as a way to help prevent simultaneous updates of a rule set from overwriting each other. It is strongly suggested that systems make use of the etag in the read -> modify -> write pattern to perform rule set updates in order to avoid race conditions that is get an etag from a GET rule set request, and pass it with the PUT update request to identify the rule set version you are updating.""" grant_rules: Optional[List[GrantRule]] = None def as_dict(self) -> dict: """Serializes the RuleSetUpdateRequest into a dictionary suitable for use as a JSON request body.""" body = {} if self.etag is not None: body["etag"] = self.etag if self.grant_rules: body["grant_rules"] = [v.as_dict() for v in self.grant_rules] if self.name is not None: body["name"] = self.name return body def as_shallow_dict(self) -> dict: """Serializes the RuleSetUpdateRequest into a shallow dictionary of its immediate attributes.""" body = {} if self.etag is not None: body["etag"] = self.etag if self.grant_rules: body["grant_rules"] = self.grant_rules if self.name is not None: body["name"] = self.name return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> RuleSetUpdateRequest: """Deserializes the RuleSetUpdateRequest from a dictionary.""" return cls( etag=d.get("etag", None), grant_rules=_repeated_dict(d, "grant_rules", GrantRule), name=d.get("name", None) ) @dataclass class ServicePrincipal: active: Optional[bool] = None """If this user is active""" application_id: Optional[str] = None """UUID relating to the service principal""" display_name: Optional[str] = None """String that represents a concatenation of given and family names.""" entitlements: Optional[List[ComplexValue]] = None """Entitlements assigned to the service principal. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements""" external_id: Optional[str] = None groups: Optional[List[ComplexValue]] = None id: Optional[str] = None """Databricks service principal ID.""" roles: Optional[List[ComplexValue]] = None """Corresponds to AWS instance profile/arn role.""" schemas: Optional[List[ServicePrincipalSchema]] = None """The schema of the List response.""" def as_dict(self) -> dict: """Serializes the ServicePrincipal into a dictionary suitable for use as a JSON request body.""" body = {} if self.active is not None: body["active"] = self.active if self.application_id is not None: body["applicationId"] = self.application_id if self.display_name is not None: body["displayName"] = self.display_name if self.entitlements: body["entitlements"] = [v.as_dict() for v in self.entitlements] if self.external_id is not None: body["externalId"] = self.external_id if self.groups: body["groups"] = [v.as_dict() for v in self.groups] if self.id is not None: body["id"] = self.id if self.roles: body["roles"] = [v.as_dict() for v in self.roles] if self.schemas: body["schemas"] = [v.value for v in self.schemas] return body def as_shallow_dict(self) -> dict: """Serializes the ServicePrincipal into a shallow dictionary of its immediate attributes.""" body = {} if self.active is not None: body["active"] = self.active if self.application_id is not None: body["applicationId"] = self.application_id if self.display_name is not None: body["displayName"] = self.display_name if self.entitlements: body["entitlements"] = self.entitlements if self.external_id is not None: body["externalId"] = self.external_id if self.groups: body["groups"] = self.groups if self.id is not None: body["id"] = self.id if self.roles: body["roles"] = self.roles if self.schemas: body["schemas"] = self.schemas return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> ServicePrincipal: """Deserializes the ServicePrincipal from a dictionary.""" return cls( active=d.get("active", None), application_id=d.get("applicationId", None), display_name=d.get("displayName", None), entitlements=_repeated_dict(d, "entitlements", ComplexValue), external_id=d.get("externalId", None), groups=_repeated_dict(d, "groups", ComplexValue), id=d.get("id", None), roles=_repeated_dict(d, "roles", ComplexValue), schemas=_repeated_enum(d, "schemas", ServicePrincipalSchema), ) class ServicePrincipalSchema(Enum): URN_IETF_PARAMS_SCIM_SCHEMAS_CORE_2_0_SERVICE_PRINCIPAL = "urn:ietf:params:scim:schemas:core:2.0:ServicePrincipal" @dataclass class User: active: Optional[bool] = None """If this user is active""" display_name: Optional[str] = None """String that represents a concatenation of given and family names. For example `John Smith`. This field cannot be updated through the Workspace SCIM APIs when [identity federation is enabled]. Use Account SCIM APIs to update `displayName`. [identity federation is enabled]: https://docs.databricks.com/administration-guide/users-groups/best-practices.html#enable-identity-federation""" emails: Optional[List[ComplexValue]] = None """All the emails associated with the Databricks user.""" entitlements: Optional[List[ComplexValue]] = None """Entitlements assigned to the user. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements""" external_id: Optional[str] = None """External ID is not currently supported. It is reserved for future use.""" groups: Optional[List[ComplexValue]] = None id: Optional[str] = None """Databricks user ID.""" name: Optional[Name] = None roles: Optional[List[ComplexValue]] = None """Corresponds to AWS instance profile/arn role.""" schemas: Optional[List[UserSchema]] = None """The schema of the user.""" user_name: Optional[str] = None """Email address of the Databricks user.""" def as_dict(self) -> dict: """Serializes the User into a dictionary suitable for use as a JSON request body.""" body = {} if self.active is not None: body["active"] = self.active if self.display_name is not None: body["displayName"] = self.display_name if self.emails: body["emails"] = [v.as_dict() for v in self.emails] if self.entitlements: body["entitlements"] = [v.as_dict() for v in self.entitlements] if self.external_id is not None: body["externalId"] = self.external_id if self.groups: body["groups"] = [v.as_dict() for v in self.groups] if self.id is not None: body["id"] = self.id if self.name: body["name"] = self.name.as_dict() if self.roles: body["roles"] = [v.as_dict() for v in self.roles] if self.schemas: body["schemas"] = [v.value for v in self.schemas] if self.user_name is not None: body["userName"] = self.user_name return body def as_shallow_dict(self) -> dict: """Serializes the User into a shallow dictionary of its immediate attributes.""" body = {} if self.active is not None: body["active"] = self.active if self.display_name is not None: body["displayName"] = self.display_name if self.emails: body["emails"] = self.emails if self.entitlements: body["entitlements"] = self.entitlements if self.external_id is not None: body["externalId"] = self.external_id if self.groups: body["groups"] = self.groups if self.id is not None: body["id"] = self.id if self.name: body["name"] = self.name if self.roles: body["roles"] = self.roles if self.schemas: body["schemas"] = self.schemas if self.user_name is not None: body["userName"] = self.user_name return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> User: """Deserializes the User from a dictionary.""" return cls( active=d.get("active", None), display_name=d.get("displayName", None), emails=_repeated_dict(d, "emails", ComplexValue), entitlements=_repeated_dict(d, "entitlements", ComplexValue), external_id=d.get("externalId", None), groups=_repeated_dict(d, "groups", ComplexValue), id=d.get("id", None), name=_from_dict(d, "name", Name), roles=_repeated_dict(d, "roles", ComplexValue), schemas=_repeated_enum(d, "schemas", UserSchema), user_name=d.get("userName", None), ) class UserSchema(Enum): URN_IETF_PARAMS_SCIM_SCHEMAS_CORE_2_0_USER = "urn:ietf:params:scim:schemas:core:2.0:User" URN_IETF_PARAMS_SCIM_SCHEMAS_EXTENSION_WORKSPACE_2_0_USER = ( "urn:ietf:params:scim:schemas:extension:workspace:2.0:User" ) class WorkspacePermission(Enum): ADMIN = "ADMIN" UNKNOWN = "UNKNOWN" USER = "USER" @dataclass class WorkspacePermissions: permissions: Optional[List[PermissionOutput]] = None """Array of permissions defined for a workspace.""" def as_dict(self) -> dict: """Serializes the WorkspacePermissions into a dictionary suitable for use as a JSON request body.""" body = {} if self.permissions: body["permissions"] = [v.as_dict() for v in self.permissions] return body def as_shallow_dict(self) -> dict: """Serializes the WorkspacePermissions into a shallow dictionary of its immediate attributes.""" body = {} if self.permissions: body["permissions"] = self.permissions return body @classmethod def from_dict(cls, d: Dict[str, Any]) -> WorkspacePermissions: """Deserializes the WorkspacePermissions from a dictionary.""" return cls(permissions=_repeated_dict(d, "permissions", PermissionOutput)) class AccessControlAPI: """Rule based Access Control for Databricks Resources.""" def __init__(self, api_client): self._api = api_client def check_policy( self, actor: Actor, permission: str, resource: str, consistency_token: ConsistencyToken, authz_identity: RequestAuthzIdentity, *, resource_info: Optional[ResourceInfo] = None, ) -> CheckPolicyResponse: """Check access policy to a resource. :param actor: :class:`Actor` :param permission: str :param resource: str Ex: (servicePrincipal/use, accounts//servicePrincipals/) Ex: (servicePrincipal.ruleSet/update, accounts//servicePrincipals//ruleSets/default) :param consistency_token: :class:`ConsistencyToken` :param authz_identity: :class:`RequestAuthzIdentity` :param resource_info: :class:`ResourceInfo` (optional) :returns: :class:`CheckPolicyResponse` """ query = {} if actor is not None: query["actor"] = actor.as_dict() if authz_identity is not None: query["authz_identity"] = authz_identity.value if consistency_token is not None: query["consistency_token"] = consistency_token.as_dict() if permission is not None: query["permission"] = permission if resource is not None: query["resource"] = resource if resource_info is not None: query["resource_info"] = resource_info.as_dict() headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("GET", "/api/2.0/access-control/check-policy-v2", query=query, headers=headers) return CheckPolicyResponse.from_dict(res) class AccountAccessControlAPI: """These APIs manage access rules on resources in an account. Currently, only grant rules are supported. A grant rule specifies a role assigned to a set of principals. A list of rules attached to a resource is called a rule set.""" def __init__(self, api_client): self._api = api_client def get_assignable_roles_for_resource(self, resource: str) -> GetAssignableRolesForResourceResponse: """Gets all the roles that can be granted on an account level resource. A role is grantable if the rule set on the resource can contain an access rule of the role. :param resource: str The resource name for which assignable roles will be listed. Examples | Summary :--- | :--- `resource=accounts/` | A resource name for the account. `resource=accounts//groups/` | A resource name for the group. `resource=accounts//servicePrincipals/` | A resource name for the service principal. `resource=accounts//tagPolicies/` | A resource name for the tag policy. :returns: :class:`GetAssignableRolesForResourceResponse` """ query = {} if resource is not None: query["resource"] = resource headers = { "Accept": "application/json", } res = self._api.do( "GET", f"/api/2.0/preview/accounts/{self._api.account_id}/access-control/assignable-roles", query=query, headers=headers, ) return GetAssignableRolesForResourceResponse.from_dict(res) def get_rule_set(self, name: str, etag: str) -> RuleSetResponse: """Get a rule set by its name. A rule set is always attached to a resource and contains a list of access rules on the said resource. Currently only a default rule set for each resource is supported. :param name: str The ruleset name associated with the request. Examples | Summary :--- | :--- `name=accounts//ruleSets/default` | A name for a rule set on the account. `name=accounts//groups//ruleSets/default` | A name for a rule set on the group. `name=accounts//servicePrincipals//ruleSets/default` | A name for a rule set on the service principal. `name=accounts//tagPolicies//ruleSets/default` | A name for a rule set on the tag policy. :param etag: str Etag used for versioning. The response is at least as fresh as the eTag provided. Etag is used for optimistic concurrency control as a way to help prevent simultaneous updates of a rule set from overwriting each other. It is strongly suggested that systems make use of the etag in the read -> modify -> write pattern to perform rule set updates in order to avoid race conditions that is get an etag from a GET rule set request, and pass it with the PUT update request to identify the rule set version you are updating. Examples | Summary :--- | :--- `etag=` | An empty etag can only be used in GET to indicate no freshness requirements. `etag=RENUAAABhSweA4NvVmmUYdiU717H3Tgy0UJdor3gE4a+mq/oj9NjAf8ZsQ==` | An etag encoded a specific version of the rule set to get or to be updated. :returns: :class:`RuleSetResponse` """ query = {} if etag is not None: query["etag"] = etag if name is not None: query["name"] = name headers = { "Accept": "application/json", } res = self._api.do( "GET", f"/api/2.0/preview/accounts/{self._api.account_id}/access-control/rule-sets", query=query, headers=headers, ) return RuleSetResponse.from_dict(res) def update_rule_set(self, name: str, rule_set: RuleSetUpdateRequest) -> RuleSetResponse: """Replace the rules of a rule set. First, use get to read the current version of the rule set before modifying it. This pattern helps prevent conflicts between concurrent updates. :param name: str Name of the rule set. :param rule_set: :class:`RuleSetUpdateRequest` :returns: :class:`RuleSetResponse` """ body = {} if name is not None: body["name"] = name if rule_set is not None: body["rule_set"] = rule_set.as_dict() headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do( "PUT", f"/api/2.0/preview/accounts/{self._api.account_id}/access-control/rule-sets", body=body, headers=headers, ) return RuleSetResponse.from_dict(res) class AccountAccessControlProxyAPI: """These APIs manage access rules on resources in an account. Currently, only grant rules are supported. A grant rule specifies a role assigned to a set of principals. A list of rules attached to a resource is called a rule set. A workspace must belong to an account for these APIs to work""" def __init__(self, api_client): self._api = api_client def get_assignable_roles_for_resource(self, resource: str) -> GetAssignableRolesForResourceResponse: """Gets all the roles that can be granted on an account level resource. A role is grantable if the rule set on the resource can contain an access rule of the role. :param resource: str The resource name for which assignable roles will be listed. Examples | Summary :--- | :--- `resource=accounts/` | A resource name for the account. `resource=accounts//groups/` | A resource name for the group. `resource=accounts//servicePrincipals/` | A resource name for the service principal. `resource=accounts//tagPolicies/` | A resource name for the tag policy. :returns: :class:`GetAssignableRolesForResourceResponse` """ query = {} if resource is not None: query["resource"] = resource headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do( "GET", "/api/2.0/preview/accounts/access-control/assignable-roles", query=query, headers=headers ) return GetAssignableRolesForResourceResponse.from_dict(res) def get_rule_set(self, name: str, etag: str) -> RuleSetResponse: """Get a rule set by its name. A rule set is always attached to a resource and contains a list of access rules on the said resource. Currently only a default rule set for each resource is supported. :param name: str The ruleset name associated with the request. Examples | Summary :--- | :--- `name=accounts//ruleSets/default` | A name for a rule set on the account. `name=accounts//groups//ruleSets/default` | A name for a rule set on the group. `name=accounts//servicePrincipals//ruleSets/default` | A name for a rule set on the service principal. `name=accounts//tagPolicies//ruleSets/default` | A name for a rule set on the tag policy. :param etag: str Etag used for versioning. The response is at least as fresh as the eTag provided. Etag is used for optimistic concurrency control as a way to help prevent simultaneous updates of a rule set from overwriting each other. It is strongly suggested that systems make use of the etag in the read -> modify -> write pattern to perform rule set updates in order to avoid race conditions that is get an etag from a GET rule set request, and pass it with the PUT update request to identify the rule set version you are updating. Examples | Summary :--- | :--- `etag=` | An empty etag can only be used in GET to indicate no freshness requirements. `etag=RENUAAABhSweA4NvVmmUYdiU717H3Tgy0UJdor3gE4a+mq/oj9NjAf8ZsQ==` | An etag encoded a specific version of the rule set to get or to be updated. :returns: :class:`RuleSetResponse` """ query = {} if etag is not None: query["etag"] = etag if name is not None: query["name"] = name headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("GET", "/api/2.0/preview/accounts/access-control/rule-sets", query=query, headers=headers) return RuleSetResponse.from_dict(res) def update_rule_set(self, name: str, rule_set: RuleSetUpdateRequest) -> RuleSetResponse: """Replace the rules of a rule set. First, use get to read the current version of the rule set before modifying it. This pattern helps prevent conflicts between concurrent updates. :param name: str Name of the rule set. :param rule_set: :class:`RuleSetUpdateRequest` :returns: :class:`RuleSetResponse` """ body = {} if name is not None: body["name"] = name if rule_set is not None: body["rule_set"] = rule_set.as_dict() headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("PUT", "/api/2.0/preview/accounts/access-control/rule-sets", body=body, headers=headers) return RuleSetResponse.from_dict(res) class AccountGroupsV2API: """Groups simplify identity management, making it easier to assign access to Databricks account, data, and other securable objects. It is best practice to assign access to workspaces and access-control policies in Unity Catalog to groups, instead of to users individually. All Databricks account identities can be assigned as members of groups, and members inherit permissions that are assigned to their group.""" def __init__(self, api_client): self._api = api_client def create( self, *, display_name: Optional[str] = None, external_id: Optional[str] = None, id: Optional[str] = None, members: Optional[List[ComplexValue]] = None, meta: Optional[ResourceMeta] = None, roles: Optional[List[ComplexValue]] = None, ) -> AccountGroup: """Creates a group in the Databricks account with a unique name, using the supplied group details. :param display_name: str (optional) String that represents a human-readable group name :param external_id: str (optional) :param id: str (optional) Databricks group ID :param members: List[:class:`ComplexValue`] (optional) :param meta: :class:`ResourceMeta` (optional) Container for the group identifier. Workspace local versus account. :param roles: List[:class:`ComplexValue`] (optional) Indicates if the group has the admin role. :returns: :class:`AccountGroup` """ body = {} if display_name is not None: body["displayName"] = display_name if external_id is not None: body["externalId"] = external_id if id is not None: body["id"] = id if members is not None: body["members"] = [v.as_dict() for v in members] if meta is not None: body["meta"] = meta.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do( "POST", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Groups", body=body, headers=headers ) return AccountGroup.from_dict(res) def delete(self, id: str): """Deletes a group from the Databricks account. :param id: str Unique ID for a group in the Databricks account. """ headers = {} self._api.do("DELETE", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Groups/{id}", headers=headers) def get(self, id: str) -> AccountGroup: """Gets the information for a specific group in the Databricks account. :param id: str Unique ID for a group in the Databricks account. :returns: :class:`AccountGroup` """ headers = { "Accept": "application/json", } res = self._api.do("GET", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Groups/{id}", headers=headers) return AccountGroup.from_dict(res) def list( self, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[ListSortOrder] = None, start_index: Optional[int] = None, ) -> Iterator[AccountGroup]: """Gets all details of the groups associated with the Databricks account. As of 08/22/2025, this endpoint will no longer return members. Instead, members should be retrieved by iterating through `Get group details`. Existing accounts that rely on this attribute will not be impacted and will continue receiving member data as before. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. Default is 10000. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. :param sort_order: :class:`ListSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: Iterator over :class:`AccountGroup` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } query["startIndex"] = 1 if "count" not in query: query["count"] = 10000 while True: json = self._api.do( "GET", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Groups", query=query, headers=headers ) if "Resources" in json: for v in json["Resources"]: yield AccountGroup.from_dict(v) if "Resources" not in json or not json["Resources"]: return query["startIndex"] += len(json["Resources"]) def patch(self, id: str, *, operations: Optional[List[Patch]] = None, schemas: Optional[List[PatchSchema]] = None): """Partially updates the details of a group. :param id: str Unique ID in the Databricks workspace. :param operations: List[:class:`Patch`] (optional) :param schemas: List[:class:`PatchSchema`] (optional) The schema of the patch request. Must be ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]. """ body = {} if operations is not None: body["Operations"] = [v.as_dict() for v in operations] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Content-Type": "application/json", } self._api.do( "PATCH", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Groups/{id}", body=body, headers=headers ) def update( self, id: str, *, display_name: Optional[str] = None, external_id: Optional[str] = None, members: Optional[List[ComplexValue]] = None, meta: Optional[ResourceMeta] = None, roles: Optional[List[ComplexValue]] = None, ): """Updates the details of a group by replacing the entire group entity. :param id: str Databricks group ID :param display_name: str (optional) String that represents a human-readable group name :param external_id: str (optional) :param members: List[:class:`ComplexValue`] (optional) :param meta: :class:`ResourceMeta` (optional) Container for the group identifier. Workspace local versus account. :param roles: List[:class:`ComplexValue`] (optional) Indicates if the group has the admin role. """ body = {} if display_name is not None: body["displayName"] = display_name if external_id is not None: body["externalId"] = external_id if members is not None: body["members"] = [v.as_dict() for v in members] if meta is not None: body["meta"] = meta.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] headers = { "Accept": "application/json", "Content-Type": "application/json", } self._api.do("PUT", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Groups/{id}", body=body, headers=headers) class AccountServicePrincipalsV2API: """Identities for use with jobs, automated tools, and systems such as scripts, apps, and CI/CD platforms. Databricks recommends creating service principals to run production jobs or modify production data. If all processes that act on production data run with service principals, interactive users do not need any write, delete, or modify privileges in production. This eliminates the risk of a user overwriting production data by accident.""" def __init__(self, api_client): self._api = api_client def create( self, *, active: Optional[bool] = None, application_id: Optional[str] = None, display_name: Optional[str] = None, external_id: Optional[str] = None, id: Optional[str] = None, roles: Optional[List[ComplexValue]] = None, ) -> AccountServicePrincipal: """Creates a new service principal in the Databricks account. :param active: bool (optional) If this user is active :param application_id: str (optional) UUID relating to the service principal :param display_name: str (optional) String that represents a concatenation of given and family names. :param external_id: str (optional) :param id: str (optional) Databricks service principal ID. :param roles: List[:class:`ComplexValue`] (optional) Indicates if the group has the admin role. :returns: :class:`AccountServicePrincipal` """ body = {} if active is not None: body["active"] = active if application_id is not None: body["applicationId"] = application_id if display_name is not None: body["displayName"] = display_name if external_id is not None: body["externalId"] = external_id if id is not None: body["id"] = id if roles is not None: body["roles"] = [v.as_dict() for v in roles] headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do( "POST", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/ServicePrincipals", body=body, headers=headers ) return AccountServicePrincipal.from_dict(res) def delete(self, id: str): """Delete a single service principal in the Databricks account. :param id: str Unique ID for a service principal in the Databricks account. """ headers = {} self._api.do( "DELETE", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/ServicePrincipals/{id}", headers=headers ) def get(self, id: str) -> AccountServicePrincipal: """Gets the details for a single service principal define in the Databricks account. :param id: str Unique ID for a service principal in the Databricks account. :returns: :class:`AccountServicePrincipal` """ headers = { "Accept": "application/json", } res = self._api.do( "GET", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/ServicePrincipals/{id}", headers=headers ) return AccountServicePrincipal.from_dict(res) def list( self, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[ListSortOrder] = None, start_index: Optional[int] = None, ) -> Iterator[AccountServicePrincipal]: """Gets the set of service principals associated with a Databricks account. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. Default is 10000. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. :param sort_order: :class:`ListSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: Iterator over :class:`AccountServicePrincipal` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } query["startIndex"] = 1 if "count" not in query: query["count"] = 10000 while True: json = self._api.do( "GET", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/ServicePrincipals", query=query, headers=headers, ) if "Resources" in json: for v in json["Resources"]: yield AccountServicePrincipal.from_dict(v) if "Resources" not in json or not json["Resources"]: return query["startIndex"] += len(json["Resources"]) def patch(self, id: str, *, operations: Optional[List[Patch]] = None, schemas: Optional[List[PatchSchema]] = None): """Partially updates the details of a single service principal in the Databricks account. :param id: str Unique ID in the Databricks workspace. :param operations: List[:class:`Patch`] (optional) :param schemas: List[:class:`PatchSchema`] (optional) The schema of the patch request. Must be ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]. """ body = {} if operations is not None: body["Operations"] = [v.as_dict() for v in operations] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } self._api.do( "PATCH", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/ServicePrincipals/{id}", body=body, headers=headers, ) def update( self, id: str, *, active: Optional[bool] = None, application_id: Optional[str] = None, display_name: Optional[str] = None, external_id: Optional[str] = None, roles: Optional[List[ComplexValue]] = None, ): """Updates the details of a single service principal. This action replaces the existing service principal with the same name. :param id: str Databricks service principal ID. :param active: bool (optional) If this user is active :param application_id: str (optional) UUID relating to the service principal :param display_name: str (optional) String that represents a concatenation of given and family names. :param external_id: str (optional) :param roles: List[:class:`ComplexValue`] (optional) Indicates if the group has the admin role. """ body = {} if active is not None: body["active"] = active if application_id is not None: body["applicationId"] = application_id if display_name is not None: body["displayName"] = display_name if external_id is not None: body["externalId"] = external_id if roles is not None: body["roles"] = [v.as_dict() for v in roles] headers = { "Accept": "application/json", "Content-Type": "application/json", } self._api.do( "PUT", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/ServicePrincipals/{id}", body=body, headers=headers, ) class AccountUsersV2API: """User identities recognized by Databricks and represented by email addresses. Databricks recommends using SCIM provisioning to sync users and groups automatically from your identity provider to your Databricks account. SCIM streamlines onboarding a new employee or team by using your identity provider to create users and groups in Databricks account and give them the proper level of access. When a user leaves your organization or no longer needs access to Databricks account, admins can terminate the user in your identity provider and that user’s account will also be removed from Databricks account. This ensures a consistent offboarding process and prevents unauthorized users from accessing sensitive data.""" def __init__(self, api_client): self._api = api_client def create( self, *, active: Optional[bool] = None, display_name: Optional[str] = None, emails: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, id: Optional[str] = None, name: Optional[Name] = None, roles: Optional[List[ComplexValue]] = None, user_name: Optional[str] = None, ) -> AccountUser: """Creates a new user in the Databricks account. This new user will also be added to the Databricks account. :param active: bool (optional) If this user is active :param display_name: str (optional) String that represents a concatenation of given and family names. For example `John Smith`. :param emails: List[:class:`ComplexValue`] (optional) All the emails associated with the Databricks user. :param external_id: str (optional) External ID is not currently supported. It is reserved for future use. :param id: str (optional) Databricks user ID. :param name: :class:`Name` (optional) :param roles: List[:class:`ComplexValue`] (optional) Indicates if the group has the admin role. :param user_name: str (optional) Email address of the Databricks user. :returns: :class:`AccountUser` """ body = {} if active is not None: body["active"] = active if display_name is not None: body["displayName"] = display_name if emails is not None: body["emails"] = [v.as_dict() for v in emails] if external_id is not None: body["externalId"] = external_id if id is not None: body["id"] = id if name is not None: body["name"] = name.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if user_name is not None: body["userName"] = user_name headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do( "POST", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Users", body=body, headers=headers ) return AccountUser.from_dict(res) def delete(self, id: str): """Deletes a user. Deleting a user from a Databricks account also removes objects associated with the user. :param id: str Unique ID for a user in the Databricks account. """ headers = {} self._api.do("DELETE", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Users/{id}", headers=headers) def get( self, id: str, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[GetSortOrder] = None, start_index: Optional[int] = None, ) -> AccountUser: """Gets information for a specific user in Databricks account. :param id: str Unique ID for a user in the Databricks account. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. Default is 10000. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. Multi-part paths are supported. For example, `userName`, `name.givenName`, and `emails`. :param sort_order: :class:`GetSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: :class:`AccountUser` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } res = self._api.do( "GET", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Users/{id}", query=query, headers=headers ) return AccountUser.from_dict(res) def list( self, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[ListSortOrder] = None, start_index: Optional[int] = None, ) -> Iterator[AccountUser]: """Gets details for all the users associated with a Databricks account. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. Default is 10000. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. Multi-part paths are supported. For example, `userName`, `name.givenName`, and `emails`. :param sort_order: :class:`ListSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: Iterator over :class:`AccountUser` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } query["startIndex"] = 1 if "count" not in query: query["count"] = 10000 while True: json = self._api.do( "GET", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Users", query=query, headers=headers ) if "Resources" in json: for v in json["Resources"]: yield AccountUser.from_dict(v) if "Resources" not in json or not json["Resources"]: return query["startIndex"] += len(json["Resources"]) def patch(self, id: str, *, operations: Optional[List[Patch]] = None, schemas: Optional[List[PatchSchema]] = None): """Partially updates a user resource by applying the supplied operations on specific user attributes. :param id: str Unique ID in the Databricks workspace. :param operations: List[:class:`Patch`] (optional) :param schemas: List[:class:`PatchSchema`] (optional) The schema of the patch request. Must be ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]. """ body = {} if operations is not None: body["Operations"] = [v.as_dict() for v in operations] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } self._api.do( "PATCH", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Users/{id}", body=body, headers=headers ) def update( self, id: str, *, active: Optional[bool] = None, display_name: Optional[str] = None, emails: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, name: Optional[Name] = None, roles: Optional[List[ComplexValue]] = None, user_name: Optional[str] = None, ): """Replaces a user's information with the data supplied in request. :param id: str Databricks user ID. :param active: bool (optional) If this user is active :param display_name: str (optional) String that represents a concatenation of given and family names. For example `John Smith`. :param emails: List[:class:`ComplexValue`] (optional) All the emails associated with the Databricks user. :param external_id: str (optional) External ID is not currently supported. It is reserved for future use. :param name: :class:`Name` (optional) :param roles: List[:class:`ComplexValue`] (optional) Indicates if the group has the admin role. :param user_name: str (optional) Email address of the Databricks user. """ body = {} if active is not None: body["active"] = active if display_name is not None: body["displayName"] = display_name if emails is not None: body["emails"] = [v.as_dict() for v in emails] if external_id is not None: body["externalId"] = external_id if name is not None: body["name"] = name.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if user_name is not None: body["userName"] = user_name headers = { "Accept": "application/json", "Content-Type": "application/json", } self._api.do("PUT", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Users/{id}", body=body, headers=headers) class CurrentUserAPI: """This API allows retrieving information about currently authenticated user or service principal.""" def __init__(self, api_client): self._api = api_client def me(self) -> User: """Get details about the current method caller's identity. :returns: :class:`User` """ headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("GET", "/api/2.0/preview/scim/v2/Me", headers=headers) return User.from_dict(res) class GroupsV2API: """Groups simplify identity management, making it easier to assign access to Databricks workspace, data, and other securable objects. It is best practice to assign access to workspaces and access-control policies in Unity Catalog to groups, instead of to users individually. All Databricks workspace identities can be assigned as members of groups, and members inherit permissions that are assigned to their group.""" def __init__(self, api_client): self._api = api_client def create( self, *, display_name: Optional[str] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, id: Optional[str] = None, members: Optional[List[ComplexValue]] = None, meta: Optional[ResourceMeta] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[GroupSchema]] = None, ) -> Group: """Creates a group in the Databricks workspace with a unique name, using the supplied group details. :param display_name: str (optional) String that represents a human-readable group name :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the group. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) :param groups: List[:class:`ComplexValue`] (optional) :param id: str (optional) Databricks group ID :param members: List[:class:`ComplexValue`] (optional) :param meta: :class:`ResourceMeta` (optional) Container for the group identifier. Workspace local versus account. :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`GroupSchema`] (optional) The schema of the group. :returns: :class:`Group` """ body = {} if display_name is not None: body["displayName"] = display_name if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if id is not None: body["id"] = id if members is not None: body["members"] = [v.as_dict() for v in members] if meta is not None: body["meta"] = meta.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("POST", "/api/2.0/preview/scim/v2/Groups", body=body, headers=headers) return Group.from_dict(res) def delete(self, id: str): """Deletes a group from the Databricks workspace. :param id: str Unique ID for a group in the Databricks workspace. """ headers = {} cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id self._api.do("DELETE", f"/api/2.0/preview/scim/v2/Groups/{id}", headers=headers) def get(self, id: str) -> Group: """Gets the information for a specific group in the Databricks workspace. :param id: str Unique ID for a group in the Databricks workspace. :returns: :class:`Group` """ headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("GET", f"/api/2.0/preview/scim/v2/Groups/{id}", headers=headers) return Group.from_dict(res) def list( self, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[ListSortOrder] = None, start_index: Optional[int] = None, ) -> Iterator[Group]: """Gets all details of the groups associated with the Databricks workspace. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. :param sort_order: :class:`ListSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: Iterator over :class:`Group` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id query["startIndex"] = 1 if "count" not in query: query["count"] = 10000 while True: json = self._api.do("GET", "/api/2.0/preview/scim/v2/Groups", query=query, headers=headers) if "Resources" in json: for v in json["Resources"]: yield Group.from_dict(v) if "Resources" not in json or not json["Resources"]: return query["startIndex"] += len(json["Resources"]) def patch(self, id: str, *, operations: Optional[List[Patch]] = None, schemas: Optional[List[PatchSchema]] = None): """Partially updates the details of a group. :param id: str Unique ID in the Databricks workspace. :param operations: List[:class:`Patch`] (optional) :param schemas: List[:class:`PatchSchema`] (optional) The schema of the patch request. Must be ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]. """ body = {} if operations is not None: body["Operations"] = [v.as_dict() for v in operations] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id self._api.do("PATCH", f"/api/2.0/preview/scim/v2/Groups/{id}", body=body, headers=headers) def update( self, id: str, *, display_name: Optional[str] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, members: Optional[List[ComplexValue]] = None, meta: Optional[ResourceMeta] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[GroupSchema]] = None, ): """Updates the details of a group by replacing the entire group entity. :param id: str Databricks group ID :param display_name: str (optional) String that represents a human-readable group name :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the group. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) :param groups: List[:class:`ComplexValue`] (optional) :param members: List[:class:`ComplexValue`] (optional) :param meta: :class:`ResourceMeta` (optional) Container for the group identifier. Workspace local versus account. :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`GroupSchema`] (optional) The schema of the group. """ body = {} if display_name is not None: body["displayName"] = display_name if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if members is not None: body["members"] = [v.as_dict() for v in members] if meta is not None: body["meta"] = meta.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id self._api.do("PUT", f"/api/2.0/preview/scim/v2/Groups/{id}", body=body, headers=headers) class PermissionMigrationAPI: """APIs for migrating acl permissions, used only by the ucx tool: https://github.com/databrickslabs/ucx""" def __init__(self, api_client): self._api = api_client def migrate_permissions( self, workspace_id: int, from_workspace_group_name: str, to_account_group_name: str, *, size: Optional[int] = None, ) -> MigratePermissionsResponse: """Migrate Permissions. :param workspace_id: int WorkspaceId of the associated workspace where the permission migration will occur. :param from_workspace_group_name: str The name of the workspace group that permissions will be migrated from. :param to_account_group_name: str The name of the account group that permissions will be migrated to. :param size: int (optional) The maximum number of permissions that will be migrated. :returns: :class:`MigratePermissionsResponse` """ body = {} if from_workspace_group_name is not None: body["from_workspace_group_name"] = from_workspace_group_name if size is not None: body["size"] = size if to_account_group_name is not None: body["to_account_group_name"] = to_account_group_name if workspace_id is not None: body["workspace_id"] = workspace_id headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("POST", "/api/2.0/permissionmigration", body=body, headers=headers) return MigratePermissionsResponse.from_dict(res) class PermissionsAPI: """Permissions API are used to create read, write, edit, update and manage access for various users on different objects and endpoints. * **[Apps permissions](:service:apps)** — Manage which users can manage or use apps. * **[Cluster permissions](:service:clusters)** — Manage which users can manage, restart, or attach to clusters. * **[Cluster policy permissions](:service:clusterpolicies)** — Manage which users can use cluster policies. * **[Delta Live Tables pipeline permissions](:service:pipelines)** — Manage which users can view, manage, run, cancel, or own a Delta Live Tables pipeline. * **[Job permissions](:service:jobs)** — Manage which users can view, manage, trigger, cancel, or own a job. * **[MLflow experiment permissions](:service:experiments)** — Manage which users can read, edit, or manage MLflow experiments. * **[MLflow registered model permissions](:service:modelregistry)** — Manage which users can read, edit, or manage MLflow registered models. * **[Instance Pool permissions](:service:instancepools)** — Manage which users can manage or attach to pools. * **[Repo permissions](repos)** — Manage which users can read, run, edit, or manage a repo. * **[Serving endpoint permissions](:service:servingendpoints)** — Manage which users can view, query, or manage a serving endpoint. * **[SQL warehouse permissions](:service:warehouses)** — Manage which users can use or manage SQL warehouses. * **[Token permissions](:service:tokenmanagement)** — Manage which users can create or use tokens. * **[Workspace object permissions](:service:workspace)** — Manage which users can read, run, edit, or manage alerts, dbsql-dashboards, directories, files, notebooks and queries. For the mapping of the required permissions for specific actions or abilities and other important information, see [Access Control]. Note that to manage access control on service principals, use **[Account Access Control Proxy](:service:accountaccesscontrolproxy)**. [Access Control]: https://docs.databricks.com/security/auth-authz/access-control/index.html""" def __init__(self, api_client): self._api = api_client def get(self, request_object_type: str, request_object_id: str) -> ObjectPermissions: """Gets the permissions of an object. Objects can inherit permissions from their parent objects or root object. :param request_object_type: str The type of the request object. Can be one of the following: alerts, alertsv2, authorization, clusters, cluster-policies, dashboards, dbsql-dashboards, directories, experiments, files, genie, instance-pools, jobs, notebooks, pipelines, queries, registered-models, repos, serving-endpoints, or warehouses. :param request_object_id: str The id of the request object. :returns: :class:`ObjectPermissions` """ headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("GET", f"/api/2.0/permissions/{request_object_type}/{request_object_id}", headers=headers) return ObjectPermissions.from_dict(res) def get_permission_levels(self, request_object_type: str, request_object_id: str) -> GetPermissionLevelsResponse: """Gets the permission levels that a user can have on an object. :param request_object_type: str The type of the request object. Can be one of the following: alerts, alertsv2, authorization, clusters, cluster-policies, dashboards, dbsql-dashboards, directories, experiments, files, genie, instance-pools, jobs, notebooks, pipelines, queries, registered-models, repos, serving-endpoints, or warehouses. :param request_object_id: str :returns: :class:`GetPermissionLevelsResponse` """ headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do( "GET", f"/api/2.0/permissions/{request_object_type}/{request_object_id}/permissionLevels", headers=headers ) return GetPermissionLevelsResponse.from_dict(res) def set( self, request_object_type: str, request_object_id: str, *, access_control_list: Optional[List[AccessControlRequest]] = None, ) -> ObjectPermissions: """Sets permissions on an object, replacing existing permissions if they exist. Deletes all direct permissions if none are specified. Objects can inherit permissions from their parent objects or root object. :param request_object_type: str The type of the request object. Can be one of the following: alerts, alertsv2, authorization, clusters, cluster-policies, dashboards, dbsql-dashboards, directories, experiments, files, genie, instance-pools, jobs, notebooks, pipelines, queries, registered-models, repos, serving-endpoints, or warehouses. :param request_object_id: str The id of the request object. :param access_control_list: List[:class:`AccessControlRequest`] (optional) :returns: :class:`ObjectPermissions` """ body = {} if access_control_list is not None: body["access_control_list"] = [v.as_dict() for v in access_control_list] headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do( "PUT", f"/api/2.0/permissions/{request_object_type}/{request_object_id}", body=body, headers=headers ) return ObjectPermissions.from_dict(res) def update( self, request_object_type: str, request_object_id: str, *, access_control_list: Optional[List[AccessControlRequest]] = None, ) -> ObjectPermissions: """Updates the permissions on an object. Objects can inherit permissions from their parent objects or root object. :param request_object_type: str The type of the request object. Can be one of the following: alerts, alertsv2, authorization, clusters, cluster-policies, dashboards, dbsql-dashboards, directories, experiments, files, genie, instance-pools, jobs, notebooks, pipelines, queries, registered-models, repos, serving-endpoints, or warehouses. :param request_object_id: str The id of the request object. :param access_control_list: List[:class:`AccessControlRequest`] (optional) :returns: :class:`ObjectPermissions` """ body = {} if access_control_list is not None: body["access_control_list"] = [v.as_dict() for v in access_control_list] headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do( "PATCH", f"/api/2.0/permissions/{request_object_type}/{request_object_id}", body=body, headers=headers ) return ObjectPermissions.from_dict(res) class ServicePrincipalsV2API: """Identities for use with jobs, automated tools, and systems such as scripts, apps, and CI/CD platforms. Databricks recommends creating service principals to run production jobs or modify production data. If all processes that act on production data run with service principals, interactive users do not need any write, delete, or modify privileges in production. This eliminates the risk of a user overwriting production data by accident.""" def __init__(self, api_client): self._api = api_client def create( self, *, active: Optional[bool] = None, application_id: Optional[str] = None, display_name: Optional[str] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, id: Optional[str] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[ServicePrincipalSchema]] = None, ) -> ServicePrincipal: """Creates a new service principal in the Databricks workspace. :param active: bool (optional) If this user is active :param application_id: str (optional) UUID relating to the service principal :param display_name: str (optional) String that represents a concatenation of given and family names. :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the service principal. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) :param groups: List[:class:`ComplexValue`] (optional) :param id: str (optional) Databricks service principal ID. :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`ServicePrincipalSchema`] (optional) The schema of the List response. :returns: :class:`ServicePrincipal` """ body = {} if active is not None: body["active"] = active if application_id is not None: body["applicationId"] = application_id if display_name is not None: body["displayName"] = display_name if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if id is not None: body["id"] = id if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("POST", "/api/2.0/preview/scim/v2/ServicePrincipals", body=body, headers=headers) return ServicePrincipal.from_dict(res) def delete(self, id: str): """Delete a single service principal in the Databricks workspace. :param id: str Unique ID for a service principal in the Databricks workspace. """ headers = {} cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id self._api.do("DELETE", f"/api/2.0/preview/scim/v2/ServicePrincipals/{id}", headers=headers) def get(self, id: str) -> ServicePrincipal: """Gets the details for a single service principal define in the Databricks workspace. :param id: str Unique ID for a service principal in the Databricks workspace. :returns: :class:`ServicePrincipal` """ headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("GET", f"/api/2.0/preview/scim/v2/ServicePrincipals/{id}", headers=headers) return ServicePrincipal.from_dict(res) def list( self, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[ListSortOrder] = None, start_index: Optional[int] = None, ) -> Iterator[ServicePrincipal]: """Gets the set of service principals associated with a Databricks workspace. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. :param sort_order: :class:`ListSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: Iterator over :class:`ServicePrincipal` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id query["startIndex"] = 1 if "count" not in query: query["count"] = 10000 while True: json = self._api.do("GET", "/api/2.0/preview/scim/v2/ServicePrincipals", query=query, headers=headers) if "Resources" in json: for v in json["Resources"]: yield ServicePrincipal.from_dict(v) if "Resources" not in json or not json["Resources"]: return query["startIndex"] += len(json["Resources"]) def patch(self, id: str, *, operations: Optional[List[Patch]] = None, schemas: Optional[List[PatchSchema]] = None): """Partially updates the details of a single service principal in the Databricks workspace. :param id: str Unique ID in the Databricks workspace. :param operations: List[:class:`Patch`] (optional) :param schemas: List[:class:`PatchSchema`] (optional) The schema of the patch request. Must be ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]. """ body = {} if operations is not None: body["Operations"] = [v.as_dict() for v in operations] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id self._api.do("PATCH", f"/api/2.0/preview/scim/v2/ServicePrincipals/{id}", body=body, headers=headers) def update( self, id: str, *, active: Optional[bool] = None, application_id: Optional[str] = None, display_name: Optional[str] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[ServicePrincipalSchema]] = None, ): """Updates the details of a single service principal. This action replaces the existing service principal with the same name. :param id: str Databricks service principal ID. :param active: bool (optional) If this user is active :param application_id: str (optional) UUID relating to the service principal :param display_name: str (optional) String that represents a concatenation of given and family names. :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the service principal. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) :param groups: List[:class:`ComplexValue`] (optional) :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`ServicePrincipalSchema`] (optional) The schema of the List response. """ body = {} if active is not None: body["active"] = active if application_id is not None: body["applicationId"] = application_id if display_name is not None: body["displayName"] = display_name if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id self._api.do("PUT", f"/api/2.0/preview/scim/v2/ServicePrincipals/{id}", body=body, headers=headers) class UsersV2API: """User identities recognized by Databricks and represented by email addresses. Databricks recommends using SCIM provisioning to sync users and groups automatically from your identity provider to your Databricks workspace. SCIM streamlines onboarding a new employee or team by using your identity provider to create users and groups in Databricks workspace and give them the proper level of access. When a user leaves your organization or no longer needs access to Databricks workspace, admins can terminate the user in your identity provider and that user’s account will also be removed from Databricks workspace. This ensures a consistent offboarding process and prevents unauthorized users from accessing sensitive data.""" def __init__(self, api_client): self._api = api_client def create( self, *, active: Optional[bool] = None, display_name: Optional[str] = None, emails: Optional[List[ComplexValue]] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, id: Optional[str] = None, name: Optional[Name] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[UserSchema]] = None, user_name: Optional[str] = None, ) -> User: """Creates a new user in the Databricks workspace. This new user will also be added to the Databricks account. :param active: bool (optional) If this user is active :param display_name: str (optional) String that represents a concatenation of given and family names. For example `John Smith`. This field cannot be updated through the Workspace SCIM APIs when [identity federation is enabled]. Use Account SCIM APIs to update `displayName`. [identity federation is enabled]: https://docs.databricks.com/administration-guide/users-groups/best-practices.html#enable-identity-federation :param emails: List[:class:`ComplexValue`] (optional) All the emails associated with the Databricks user. :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the user. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) External ID is not currently supported. It is reserved for future use. :param groups: List[:class:`ComplexValue`] (optional) :param id: str (optional) Databricks user ID. :param name: :class:`Name` (optional) :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`UserSchema`] (optional) The schema of the user. :param user_name: str (optional) Email address of the Databricks user. :returns: :class:`User` """ body = {} if active is not None: body["active"] = active if display_name is not None: body["displayName"] = display_name if emails is not None: body["emails"] = [v.as_dict() for v in emails] if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if id is not None: body["id"] = id if name is not None: body["name"] = name.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] if user_name is not None: body["userName"] = user_name headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("POST", "/api/2.0/preview/scim/v2/Users", body=body, headers=headers) return User.from_dict(res) def delete(self, id: str): """Deletes a user. Deleting a user from a Databricks workspace also removes objects associated with the user. :param id: str Unique ID for a user in the Databricks workspace. """ headers = {} cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id self._api.do("DELETE", f"/api/2.0/preview/scim/v2/Users/{id}", headers=headers) def get( self, id: str, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[GetSortOrder] = None, start_index: Optional[int] = None, ) -> User: """Gets information for a specific user in Databricks workspace. :param id: str Unique ID for a user in the Databricks workspace. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. Multi-part paths are supported. For example, `userName`, `name.givenName`, and `emails`. :param sort_order: :class:`GetSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: :class:`User` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("GET", f"/api/2.0/preview/scim/v2/Users/{id}", query=query, headers=headers) return User.from_dict(res) def get_permission_levels(self) -> GetPasswordPermissionLevelsResponse: """Gets the permission levels that a user can have on an object. :returns: :class:`GetPasswordPermissionLevelsResponse` """ headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("GET", "/api/2.0/permissions/authorization/passwords/permissionLevels", headers=headers) return GetPasswordPermissionLevelsResponse.from_dict(res) def get_permissions(self) -> PasswordPermissions: """Gets the permissions of all passwords. Passwords can inherit permissions from their root object. :returns: :class:`PasswordPermissions` """ headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("GET", "/api/2.0/permissions/authorization/passwords", headers=headers) return PasswordPermissions.from_dict(res) def list( self, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[ListSortOrder] = None, start_index: Optional[int] = None, ) -> Iterator[User]: """Gets details for all the users associated with a Databricks workspace. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. Multi-part paths are supported. For example, `userName`, `name.givenName`, and `emails`. :param sort_order: :class:`ListSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: Iterator over :class:`User` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id query["startIndex"] = 1 if "count" not in query: query["count"] = 10000 while True: json = self._api.do("GET", "/api/2.0/preview/scim/v2/Users", query=query, headers=headers) if "Resources" in json: for v in json["Resources"]: yield User.from_dict(v) if "Resources" not in json or not json["Resources"]: return query["startIndex"] += len(json["Resources"]) def patch(self, id: str, *, operations: Optional[List[Patch]] = None, schemas: Optional[List[PatchSchema]] = None): """Partially updates a user resource by applying the supplied operations on specific user attributes. :param id: str Unique ID in the Databricks workspace. :param operations: List[:class:`Patch`] (optional) :param schemas: List[:class:`PatchSchema`] (optional) The schema of the patch request. Must be ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]. """ body = {} if operations is not None: body["Operations"] = [v.as_dict() for v in operations] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id self._api.do("PATCH", f"/api/2.0/preview/scim/v2/Users/{id}", body=body, headers=headers) def set_permissions( self, *, access_control_list: Optional[List[PasswordAccessControlRequest]] = None ) -> PasswordPermissions: """Sets permissions on an object, replacing existing permissions if they exist. Deletes all direct permissions if none are specified. Objects can inherit permissions from their root object. :param access_control_list: List[:class:`PasswordAccessControlRequest`] (optional) :returns: :class:`PasswordPermissions` """ body = {} if access_control_list is not None: body["access_control_list"] = [v.as_dict() for v in access_control_list] headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("PUT", "/api/2.0/permissions/authorization/passwords", body=body, headers=headers) return PasswordPermissions.from_dict(res) def update( self, id: str, *, active: Optional[bool] = None, display_name: Optional[str] = None, emails: Optional[List[ComplexValue]] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, name: Optional[Name] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[UserSchema]] = None, user_name: Optional[str] = None, ): """Replaces a user's information with the data supplied in request. :param id: str Databricks user ID. :param active: bool (optional) If this user is active :param display_name: str (optional) String that represents a concatenation of given and family names. For example `John Smith`. This field cannot be updated through the Workspace SCIM APIs when [identity federation is enabled]. Use Account SCIM APIs to update `displayName`. [identity federation is enabled]: https://docs.databricks.com/administration-guide/users-groups/best-practices.html#enable-identity-federation :param emails: List[:class:`ComplexValue`] (optional) All the emails associated with the Databricks user. :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the user. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) External ID is not currently supported. It is reserved for future use. :param groups: List[:class:`ComplexValue`] (optional) :param name: :class:`Name` (optional) :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`UserSchema`] (optional) The schema of the user. :param user_name: str (optional) Email address of the Databricks user. """ body = {} if active is not None: body["active"] = active if display_name is not None: body["displayName"] = display_name if emails is not None: body["emails"] = [v.as_dict() for v in emails] if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if name is not None: body["name"] = name.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] if user_name is not None: body["userName"] = user_name headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id self._api.do("PUT", f"/api/2.0/preview/scim/v2/Users/{id}", body=body, headers=headers) def update_permissions( self, *, access_control_list: Optional[List[PasswordAccessControlRequest]] = None ) -> PasswordPermissions: """Updates the permissions on all passwords. Passwords can inherit permissions from their root object. :param access_control_list: List[:class:`PasswordAccessControlRequest`] (optional) :returns: :class:`PasswordPermissions` """ body = {} if access_control_list is not None: body["access_control_list"] = [v.as_dict() for v in access_control_list] headers = { "Accept": "application/json", "Content-Type": "application/json", } cfg = self._api._cfg if cfg.host_type == HostType.UNIFIED and cfg.workspace_id: headers["X-Databricks-Org-Id"] = cfg.workspace_id res = self._api.do("PATCH", "/api/2.0/permissions/authorization/passwords", body=body, headers=headers) return PasswordPermissions.from_dict(res) class WorkspaceAssignmentAPI: """The Workspace Permission Assignment API allows you to manage workspace permissions for principals in your account.""" def __init__(self, api_client): self._api = api_client def delete(self, workspace_id: int, principal_id: int): """Deletes the workspace permissions assignment in a given account and workspace for the specified principal. :param workspace_id: int The workspace ID for the account. :param principal_id: int The ID of the user, service principal, or group. """ headers = { "Accept": "application/json", } self._api.do( "DELETE", f"/api/2.0/accounts/{self._api.account_id}/workspaces/{workspace_id}/permissionassignments/principals/{principal_id}", headers=headers, ) def get(self, workspace_id: int) -> WorkspacePermissions: """Get an array of workspace permissions for the specified account and workspace. :param workspace_id: int The workspace ID. :returns: :class:`WorkspacePermissions` """ headers = { "Accept": "application/json", } res = self._api.do( "GET", f"/api/2.0/accounts/{self._api.account_id}/workspaces/{workspace_id}/permissionassignments/permissions", headers=headers, ) return WorkspacePermissions.from_dict(res) def list(self, workspace_id: int) -> Iterator[PermissionAssignment]: """Get the permission assignments for the specified Databricks account and Databricks workspace. :param workspace_id: int The workspace ID for the account. :returns: Iterator over :class:`PermissionAssignment` """ headers = { "Accept": "application/json", } json = self._api.do( "GET", f"/api/2.0/accounts/{self._api.account_id}/workspaces/{workspace_id}/permissionassignments", headers=headers, ) parsed = PermissionAssignments.from_dict(json).permission_assignments return parsed if parsed is not None else [] def update( self, workspace_id: int, principal_id: int, *, permissions: Optional[List[WorkspacePermission]] = None ) -> PermissionAssignment: """Creates or updates the workspace permissions assignment in a given account and workspace for the specified principal. :param workspace_id: int The workspace ID. :param principal_id: int The ID of the user, service principal, or group. :param permissions: List[:class:`WorkspacePermission`] (optional) Array of permissions assignments to update on the workspace. Valid values are "USER" and "ADMIN" (case-sensitive). If both "USER" and "ADMIN" are provided, "ADMIN" takes precedence. Other values will be ignored. Note that excluding this field, or providing unsupported values, will have the same effect as providing an empty list, which will result in the deletion of all permissions for the principal. :returns: :class:`PermissionAssignment` """ body = {} if permissions is not None: body["permissions"] = [v.value for v in permissions] headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do( "PUT", f"/api/2.0/accounts/{self._api.account_id}/workspaces/{workspace_id}/permissionassignments/principals/{principal_id}", body=body, headers=headers, ) return PermissionAssignment.from_dict(res) class AccountGroupsAPI: """Groups simplify identity management, making it easier to assign access to Databricks account, data, and other securable objects. It is best practice to assign access to workspaces and access-control policies in Unity Catalog to groups, instead of to users individually. All Databricks account identities can be assigned as members of groups, and members inherit permissions that are assigned to their group.""" def __init__(self, api_client): self._api = api_client def create( self, *, display_name: Optional[str] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, id: Optional[str] = None, members: Optional[List[ComplexValue]] = None, meta: Optional[ResourceMeta] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[GroupSchema]] = None, ) -> Group: """Creates a group in the Databricks account with a unique name, using the supplied group details. :param display_name: str (optional) String that represents a human-readable group name :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the group. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) :param groups: List[:class:`ComplexValue`] (optional) :param id: str (optional) Databricks group ID :param members: List[:class:`ComplexValue`] (optional) :param meta: :class:`ResourceMeta` (optional) Container for the group identifier. Workspace local versus account. :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`GroupSchema`] (optional) The schema of the group. :returns: :class:`Group` """ body = {} if display_name is not None: body["displayName"] = display_name if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if id is not None: body["id"] = id if members is not None: body["members"] = [v.as_dict() for v in members] if meta is not None: body["meta"] = meta.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do( "POST", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Groups", body=body, headers=headers ) return Group.from_dict(res) def delete(self, id: str): """Deletes a group from the Databricks account. :param id: str Unique ID for a group in the Databricks account. """ headers = {} self._api.do("DELETE", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Groups/{id}", headers=headers) def get(self, id: str) -> Group: """Gets the information for a specific group in the Databricks account. :param id: str Unique ID for a group in the Databricks account. :returns: :class:`Group` """ headers = { "Accept": "application/json", } res = self._api.do("GET", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Groups/{id}", headers=headers) return Group.from_dict(res) def list( self, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[ListSortOrder] = None, start_index: Optional[int] = None, ) -> Iterator[Group]: """Gets all details of the groups associated with the Databricks account. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. Default is 10000. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. :param sort_order: :class:`ListSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: Iterator over :class:`Group` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } # deduplicate items that may have been added during iteration seen = set() query["startIndex"] = 1 if "count" not in query: query["count"] = 10000 while True: json = self._api.do( "GET", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Groups", query=query, headers=headers ) if "Resources" in json: for v in json["Resources"]: i = v["id"] if i in seen: continue seen.add(i) yield Group.from_dict(v) if "Resources" not in json or not json["Resources"]: return query["startIndex"] += len(json["Resources"]) def patch(self, id: str, *, operations: Optional[List[Patch]] = None, schemas: Optional[List[PatchSchema]] = None): """Partially updates the details of a group. :param id: str Unique ID in the Databricks workspace. :param operations: List[:class:`Patch`] (optional) :param schemas: List[:class:`PatchSchema`] (optional) The schema of the patch request. Must be ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]. """ body = {} if operations is not None: body["Operations"] = [v.as_dict() for v in operations] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Content-Type": "application/json", } self._api.do( "PATCH", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Groups/{id}", body=body, headers=headers ) def update( self, id: str, *, display_name: Optional[str] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, members: Optional[List[ComplexValue]] = None, meta: Optional[ResourceMeta] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[GroupSchema]] = None, ): """Updates the details of a group by replacing the entire group entity. :param id: str Databricks group ID :param display_name: str (optional) String that represents a human-readable group name :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the group. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) :param groups: List[:class:`ComplexValue`] (optional) :param members: List[:class:`ComplexValue`] (optional) :param meta: :class:`ResourceMeta` (optional) Container for the group identifier. Workspace local versus account. :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`GroupSchema`] (optional) The schema of the group. """ body = {} if display_name is not None: body["displayName"] = display_name if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if members is not None: body["members"] = [v.as_dict() for v in members] if meta is not None: body["meta"] = meta.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Content-Type": "application/json", } self._api.do("PUT", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Groups/{id}", body=body, headers=headers) class AccountServicePrincipalsAPI: """Identities for use with jobs, automated tools, and systems such as scripts, apps, and CI/CD platforms. Databricks recommends creating service principals to run production jobs or modify production data. If all processes that act on production data run with service principals, interactive users do not need any write, delete, or modify privileges in production. This eliminates the risk of a user overwriting production data by accident.""" def __init__(self, api_client): self._api = api_client def create( self, *, active: Optional[bool] = None, application_id: Optional[str] = None, display_name: Optional[str] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, id: Optional[str] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[ServicePrincipalSchema]] = None, ) -> ServicePrincipal: """Creates a new service principal in the Databricks account. :param active: bool (optional) If this user is active :param application_id: str (optional) UUID relating to the service principal :param display_name: str (optional) String that represents a concatenation of given and family names. :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the service principal. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) :param groups: List[:class:`ComplexValue`] (optional) :param id: str (optional) Databricks service principal ID. :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`ServicePrincipalSchema`] (optional) The schema of the List response. :returns: :class:`ServicePrincipal` """ body = {} if active is not None: body["active"] = active if application_id is not None: body["applicationId"] = application_id if display_name is not None: body["displayName"] = display_name if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if id is not None: body["id"] = id if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do( "POST", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/ServicePrincipals", body=body, headers=headers ) return ServicePrincipal.from_dict(res) def delete(self, id: str): """Delete a single service principal in the Databricks account. :param id: str Unique ID for a service principal in the Databricks account. """ headers = {} self._api.do( "DELETE", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/ServicePrincipals/{id}", headers=headers ) def get(self, id: str) -> ServicePrincipal: """Gets the details for a single service principal define in the Databricks account. :param id: str Unique ID for a service principal in the Databricks account. :returns: :class:`ServicePrincipal` """ headers = { "Accept": "application/json", } res = self._api.do( "GET", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/ServicePrincipals/{id}", headers=headers ) return ServicePrincipal.from_dict(res) def list( self, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[ListSortOrder] = None, start_index: Optional[int] = None, ) -> Iterator[ServicePrincipal]: """Gets the set of service principals associated with a Databricks account. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. Default is 10000. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. :param sort_order: :class:`ListSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: Iterator over :class:`ServicePrincipal` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } # deduplicate items that may have been added during iteration seen = set() query["startIndex"] = 1 if "count" not in query: query["count"] = 10000 while True: json = self._api.do( "GET", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/ServicePrincipals", query=query, headers=headers, ) if "Resources" in json: for v in json["Resources"]: i = v["id"] if i in seen: continue seen.add(i) yield ServicePrincipal.from_dict(v) if "Resources" not in json or not json["Resources"]: return query["startIndex"] += len(json["Resources"]) def patch(self, id: str, *, operations: Optional[List[Patch]] = None, schemas: Optional[List[PatchSchema]] = None): """Partially updates the details of a single service principal in the Databricks account. :param id: str Unique ID in the Databricks workspace. :param operations: List[:class:`Patch`] (optional) :param schemas: List[:class:`PatchSchema`] (optional) The schema of the patch request. Must be ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]. """ body = {} if operations is not None: body["Operations"] = [v.as_dict() for v in operations] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Content-Type": "application/json", } self._api.do( "PATCH", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/ServicePrincipals/{id}", body=body, headers=headers, ) def update( self, id: str, *, active: Optional[bool] = None, application_id: Optional[str] = None, display_name: Optional[str] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[ServicePrincipalSchema]] = None, ): """Updates the details of a single service principal. This action replaces the existing service principal with the same name. :param id: str Databricks service principal ID. :param active: bool (optional) If this user is active :param application_id: str (optional) UUID relating to the service principal :param display_name: str (optional) String that represents a concatenation of given and family names. :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the service principal. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) :param groups: List[:class:`ComplexValue`] (optional) :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`ServicePrincipalSchema`] (optional) The schema of the List response. """ body = {} if active is not None: body["active"] = active if application_id is not None: body["applicationId"] = application_id if display_name is not None: body["displayName"] = display_name if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Content-Type": "application/json", } self._api.do( "PUT", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/ServicePrincipals/{id}", body=body, headers=headers, ) class AccountUsersAPI: """User identities recognized by Databricks and represented by email addresses. Databricks recommends using SCIM provisioning to sync users and groups automatically from your identity provider to your Databricks account. SCIM streamlines onboarding a new employee or team by using your identity provider to create users and groups in Databricks account and give them the proper level of access. When a user leaves your organization or no longer needs access to Databricks account, admins can terminate the user in your identity provider and that user’s account will also be removed from Databricks account. This ensures a consistent offboarding process and prevents unauthorized users from accessing sensitive data.""" def __init__(self, api_client): self._api = api_client def create( self, *, active: Optional[bool] = None, display_name: Optional[str] = None, emails: Optional[List[ComplexValue]] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, id: Optional[str] = None, name: Optional[Name] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[UserSchema]] = None, user_name: Optional[str] = None, ) -> User: """Creates a new user in the Databricks account. This new user will also be added to the Databricks account. :param active: bool (optional) If this user is active :param display_name: str (optional) String that represents a concatenation of given and family names. For example `John Smith`. This field cannot be updated through the Workspace SCIM APIs when [identity federation is enabled]. Use Account SCIM APIs to update `displayName`. [identity federation is enabled]: https://docs.databricks.com/administration-guide/users-groups/best-practices.html#enable-identity-federation :param emails: List[:class:`ComplexValue`] (optional) All the emails associated with the Databricks user. :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the user. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) External ID is not currently supported. It is reserved for future use. :param groups: List[:class:`ComplexValue`] (optional) :param id: str (optional) Databricks user ID. :param name: :class:`Name` (optional) :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`UserSchema`] (optional) The schema of the user. :param user_name: str (optional) Email address of the Databricks user. :returns: :class:`User` """ body = {} if active is not None: body["active"] = active if display_name is not None: body["displayName"] = display_name if emails is not None: body["emails"] = [v.as_dict() for v in emails] if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if id is not None: body["id"] = id if name is not None: body["name"] = name.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] if user_name is not None: body["userName"] = user_name headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do( "POST", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Users", body=body, headers=headers ) return User.from_dict(res) def delete(self, id: str): """Deletes a user. Deleting a user from a Databricks account also removes objects associated with the user. :param id: str Unique ID for a user in the Databricks account. """ headers = {} self._api.do("DELETE", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Users/{id}", headers=headers) def get( self, id: str, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[GetSortOrder] = None, start_index: Optional[int] = None, ) -> User: """Gets information for a specific user in Databricks account. :param id: str Unique ID for a user in the Databricks account. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. Default is 10000. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. Multi-part paths are supported. For example, `userName`, `name.givenName`, and `emails`. :param sort_order: :class:`GetSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: :class:`User` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } res = self._api.do( "GET", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Users/{id}", query=query, headers=headers ) return User.from_dict(res) def list( self, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[ListSortOrder] = None, start_index: Optional[int] = None, ) -> Iterator[User]: """Gets details for all the users associated with a Databricks account. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. Default is 10000. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. Multi-part paths are supported. For example, `userName`, `name.givenName`, and `emails`. :param sort_order: :class:`ListSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: Iterator over :class:`User` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } # deduplicate items that may have been added during iteration seen = set() query["startIndex"] = 1 if "count" not in query: query["count"] = 10000 while True: json = self._api.do( "GET", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Users", query=query, headers=headers ) if "Resources" in json: for v in json["Resources"]: i = v["id"] if i in seen: continue seen.add(i) yield User.from_dict(v) if "Resources" not in json or not json["Resources"]: return query["startIndex"] += len(json["Resources"]) def patch(self, id: str, *, operations: Optional[List[Patch]] = None, schemas: Optional[List[PatchSchema]] = None): """Partially updates a user resource by applying the supplied operations on specific user attributes. :param id: str Unique ID in the Databricks workspace. :param operations: List[:class:`Patch`] (optional) :param schemas: List[:class:`PatchSchema`] (optional) The schema of the patch request. Must be ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]. """ body = {} if operations is not None: body["Operations"] = [v.as_dict() for v in operations] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Content-Type": "application/json", } self._api.do( "PATCH", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Users/{id}", body=body, headers=headers ) def update( self, id: str, *, active: Optional[bool] = None, display_name: Optional[str] = None, emails: Optional[List[ComplexValue]] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, name: Optional[Name] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[UserSchema]] = None, user_name: Optional[str] = None, ): """Replaces a user's information with the data supplied in request. :param id: str Databricks user ID. :param active: bool (optional) If this user is active :param display_name: str (optional) String that represents a concatenation of given and family names. For example `John Smith`. This field cannot be updated through the Workspace SCIM APIs when [identity federation is enabled]. Use Account SCIM APIs to update `displayName`. [identity federation is enabled]: https://docs.databricks.com/administration-guide/users-groups/best-practices.html#enable-identity-federation :param emails: List[:class:`ComplexValue`] (optional) All the emails associated with the Databricks user. :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the user. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) External ID is not currently supported. It is reserved for future use. :param groups: List[:class:`ComplexValue`] (optional) :param name: :class:`Name` (optional) :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`UserSchema`] (optional) The schema of the user. :param user_name: str (optional) Email address of the Databricks user. """ body = {} if active is not None: body["active"] = active if display_name is not None: body["displayName"] = display_name if emails is not None: body["emails"] = [v.as_dict() for v in emails] if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if name is not None: body["name"] = name.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] if user_name is not None: body["userName"] = user_name headers = { "Content-Type": "application/json", } self._api.do("PUT", f"/api/2.0/accounts/{self._api.account_id}/scim/v2/Users/{id}", body=body, headers=headers) class GroupsAPI: """Groups simplify identity management, making it easier to assign access to Databricks workspace, data, and other securable objects. It is best practice to assign access to workspaces and access-control policies in Unity Catalog to groups, instead of to users individually. All Databricks workspace identities can be assigned as members of groups, and members inherit permissions that are assigned to their group.""" def __init__(self, api_client): self._api = api_client def create( self, *, display_name: Optional[str] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, id: Optional[str] = None, members: Optional[List[ComplexValue]] = None, meta: Optional[ResourceMeta] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[GroupSchema]] = None, ) -> Group: """Creates a group in the Databricks workspace with a unique name, using the supplied group details. :param display_name: str (optional) String that represents a human-readable group name :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the group. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) :param groups: List[:class:`ComplexValue`] (optional) :param id: str (optional) Databricks group ID :param members: List[:class:`ComplexValue`] (optional) :param meta: :class:`ResourceMeta` (optional) Container for the group identifier. Workspace local versus account. :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`GroupSchema`] (optional) The schema of the group. :returns: :class:`Group` """ body = {} if display_name is not None: body["displayName"] = display_name if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if id is not None: body["id"] = id if members is not None: body["members"] = [v.as_dict() for v in members] if meta is not None: body["meta"] = meta.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do("POST", "/api/2.0/preview/scim/v2/Groups", body=body, headers=headers) return Group.from_dict(res) def delete(self, id: str): """Deletes a group from the Databricks workspace. :param id: str Unique ID for a group in the Databricks workspace. """ headers = {} self._api.do("DELETE", f"/api/2.0/preview/scim/v2/Groups/{id}", headers=headers) def get(self, id: str) -> Group: """Gets the information for a specific group in the Databricks workspace. :param id: str Unique ID for a group in the Databricks workspace. :returns: :class:`Group` """ headers = { "Accept": "application/json", } res = self._api.do("GET", f"/api/2.0/preview/scim/v2/Groups/{id}", headers=headers) return Group.from_dict(res) def list( self, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[ListSortOrder] = None, start_index: Optional[int] = None, ) -> Iterator[Group]: """Gets all details of the groups associated with the Databricks workspace. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. :param sort_order: :class:`ListSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: Iterator over :class:`Group` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } # deduplicate items that may have been added during iteration seen = set() query["startIndex"] = 1 if "count" not in query: query["count"] = 10000 while True: json = self._api.do("GET", "/api/2.0/preview/scim/v2/Groups", query=query, headers=headers) if "Resources" in json: for v in json["Resources"]: i = v["id"] if i in seen: continue seen.add(i) yield Group.from_dict(v) if "Resources" not in json or not json["Resources"]: return query["startIndex"] += len(json["Resources"]) def patch(self, id: str, *, operations: Optional[List[Patch]] = None, schemas: Optional[List[PatchSchema]] = None): """Partially updates the details of a group. :param id: str Unique ID in the Databricks workspace. :param operations: List[:class:`Patch`] (optional) :param schemas: List[:class:`PatchSchema`] (optional) The schema of the patch request. Must be ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]. """ body = {} if operations is not None: body["Operations"] = [v.as_dict() for v in operations] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Content-Type": "application/json", } self._api.do("PATCH", f"/api/2.0/preview/scim/v2/Groups/{id}", body=body, headers=headers) def update( self, id: str, *, display_name: Optional[str] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, members: Optional[List[ComplexValue]] = None, meta: Optional[ResourceMeta] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[GroupSchema]] = None, ): """Updates the details of a group by replacing the entire group entity. :param id: str Databricks group ID :param display_name: str (optional) String that represents a human-readable group name :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the group. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) :param groups: List[:class:`ComplexValue`] (optional) :param members: List[:class:`ComplexValue`] (optional) :param meta: :class:`ResourceMeta` (optional) Container for the group identifier. Workspace local versus account. :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`GroupSchema`] (optional) The schema of the group. """ body = {} if display_name is not None: body["displayName"] = display_name if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if members is not None: body["members"] = [v.as_dict() for v in members] if meta is not None: body["meta"] = meta.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Content-Type": "application/json", } self._api.do("PUT", f"/api/2.0/preview/scim/v2/Groups/{id}", body=body, headers=headers) class ServicePrincipalsAPI: """Identities for use with jobs, automated tools, and systems such as scripts, apps, and CI/CD platforms. Databricks recommends creating service principals to run production jobs or modify production data. If all processes that act on production data run with service principals, interactive users do not need any write, delete, or modify privileges in production. This eliminates the risk of a user overwriting production data by accident.""" def __init__(self, api_client): self._api = api_client def create( self, *, active: Optional[bool] = None, application_id: Optional[str] = None, display_name: Optional[str] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, id: Optional[str] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[ServicePrincipalSchema]] = None, ) -> ServicePrincipal: """Creates a new service principal in the Databricks workspace. :param active: bool (optional) If this user is active :param application_id: str (optional) UUID relating to the service principal :param display_name: str (optional) String that represents a concatenation of given and family names. :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the service principal. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) :param groups: List[:class:`ComplexValue`] (optional) :param id: str (optional) Databricks service principal ID. :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`ServicePrincipalSchema`] (optional) The schema of the List response. :returns: :class:`ServicePrincipal` """ body = {} if active is not None: body["active"] = active if application_id is not None: body["applicationId"] = application_id if display_name is not None: body["displayName"] = display_name if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if id is not None: body["id"] = id if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do("POST", "/api/2.0/preview/scim/v2/ServicePrincipals", body=body, headers=headers) return ServicePrincipal.from_dict(res) def delete(self, id: str): """Delete a single service principal in the Databricks workspace. :param id: str Unique ID for a service principal in the Databricks workspace. """ headers = {} self._api.do("DELETE", f"/api/2.0/preview/scim/v2/ServicePrincipals/{id}", headers=headers) def get(self, id: str) -> ServicePrincipal: """Gets the details for a single service principal define in the Databricks workspace. :param id: str Unique ID for a service principal in the Databricks workspace. :returns: :class:`ServicePrincipal` """ headers = { "Accept": "application/json", } res = self._api.do("GET", f"/api/2.0/preview/scim/v2/ServicePrincipals/{id}", headers=headers) return ServicePrincipal.from_dict(res) def list( self, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[ListSortOrder] = None, start_index: Optional[int] = None, ) -> Iterator[ServicePrincipal]: """Gets the set of service principals associated with a Databricks workspace. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. :param sort_order: :class:`ListSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: Iterator over :class:`ServicePrincipal` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } # deduplicate items that may have been added during iteration seen = set() query["startIndex"] = 1 if "count" not in query: query["count"] = 10000 while True: json = self._api.do("GET", "/api/2.0/preview/scim/v2/ServicePrincipals", query=query, headers=headers) if "Resources" in json: for v in json["Resources"]: i = v["id"] if i in seen: continue seen.add(i) yield ServicePrincipal.from_dict(v) if "Resources" not in json or not json["Resources"]: return query["startIndex"] += len(json["Resources"]) def patch(self, id: str, *, operations: Optional[List[Patch]] = None, schemas: Optional[List[PatchSchema]] = None): """Partially updates the details of a single service principal in the Databricks workspace. :param id: str Unique ID in the Databricks workspace. :param operations: List[:class:`Patch`] (optional) :param schemas: List[:class:`PatchSchema`] (optional) The schema of the patch request. Must be ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]. """ body = {} if operations is not None: body["Operations"] = [v.as_dict() for v in operations] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Content-Type": "application/json", } self._api.do("PATCH", f"/api/2.0/preview/scim/v2/ServicePrincipals/{id}", body=body, headers=headers) def update( self, id: str, *, active: Optional[bool] = None, application_id: Optional[str] = None, display_name: Optional[str] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[ServicePrincipalSchema]] = None, ): """Updates the details of a single service principal. This action replaces the existing service principal with the same name. :param id: str Databricks service principal ID. :param active: bool (optional) If this user is active :param application_id: str (optional) UUID relating to the service principal :param display_name: str (optional) String that represents a concatenation of given and family names. :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the service principal. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) :param groups: List[:class:`ComplexValue`] (optional) :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`ServicePrincipalSchema`] (optional) The schema of the List response. """ body = {} if active is not None: body["active"] = active if application_id is not None: body["applicationId"] = application_id if display_name is not None: body["displayName"] = display_name if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Content-Type": "application/json", } self._api.do("PUT", f"/api/2.0/preview/scim/v2/ServicePrincipals/{id}", body=body, headers=headers) class UsersAPI: """User identities recognized by Databricks and represented by email addresses. Databricks recommends using SCIM provisioning to sync users and groups automatically from your identity provider to your Databricks workspace. SCIM streamlines onboarding a new employee or team by using your identity provider to create users and groups in Databricks workspace and give them the proper level of access. When a user leaves your organization or no longer needs access to Databricks workspace, admins can terminate the user in your identity provider and that user’s account will also be removed from Databricks workspace. This ensures a consistent offboarding process and prevents unauthorized users from accessing sensitive data.""" def __init__(self, api_client): self._api = api_client def create( self, *, active: Optional[bool] = None, display_name: Optional[str] = None, emails: Optional[List[ComplexValue]] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, id: Optional[str] = None, name: Optional[Name] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[UserSchema]] = None, user_name: Optional[str] = None, ) -> User: """Creates a new user in the Databricks workspace. This new user will also be added to the Databricks account. :param active: bool (optional) If this user is active :param display_name: str (optional) String that represents a concatenation of given and family names. For example `John Smith`. This field cannot be updated through the Workspace SCIM APIs when [identity federation is enabled]. Use Account SCIM APIs to update `displayName`. [identity federation is enabled]: https://docs.databricks.com/administration-guide/users-groups/best-practices.html#enable-identity-federation :param emails: List[:class:`ComplexValue`] (optional) All the emails associated with the Databricks user. :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the user. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) External ID is not currently supported. It is reserved for future use. :param groups: List[:class:`ComplexValue`] (optional) :param id: str (optional) Databricks user ID. :param name: :class:`Name` (optional) :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`UserSchema`] (optional) The schema of the user. :param user_name: str (optional) Email address of the Databricks user. :returns: :class:`User` """ body = {} if active is not None: body["active"] = active if display_name is not None: body["displayName"] = display_name if emails is not None: body["emails"] = [v.as_dict() for v in emails] if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if id is not None: body["id"] = id if name is not None: body["name"] = name.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] if user_name is not None: body["userName"] = user_name headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do("POST", "/api/2.0/preview/scim/v2/Users", body=body, headers=headers) return User.from_dict(res) def delete(self, id: str): """Deletes a user. Deleting a user from a Databricks workspace also removes objects associated with the user. :param id: str Unique ID for a user in the Databricks workspace. """ headers = {} self._api.do("DELETE", f"/api/2.0/preview/scim/v2/Users/{id}", headers=headers) def get( self, id: str, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[GetSortOrder] = None, start_index: Optional[int] = None, ) -> User: """Gets information for a specific user in Databricks workspace. :param id: str Unique ID for a user in the Databricks workspace. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. Multi-part paths are supported. For example, `userName`, `name.givenName`, and `emails`. :param sort_order: :class:`GetSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: :class:`User` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } res = self._api.do("GET", f"/api/2.0/preview/scim/v2/Users/{id}", query=query, headers=headers) return User.from_dict(res) def get_permission_levels(self) -> GetPasswordPermissionLevelsResponse: """Gets the permission levels that a user can have on an object. :returns: :class:`GetPasswordPermissionLevelsResponse` """ headers = { "Accept": "application/json", } res = self._api.do("GET", "/api/2.0/permissions/authorization/passwords/permissionLevels", headers=headers) return GetPasswordPermissionLevelsResponse.from_dict(res) def get_permissions(self) -> PasswordPermissions: """Gets the permissions of all passwords. Passwords can inherit permissions from their root object. :returns: :class:`PasswordPermissions` """ headers = { "Accept": "application/json", } res = self._api.do("GET", "/api/2.0/permissions/authorization/passwords", headers=headers) return PasswordPermissions.from_dict(res) def list( self, *, attributes: Optional[str] = None, count: Optional[int] = None, excluded_attributes: Optional[str] = None, filter: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[ListSortOrder] = None, start_index: Optional[int] = None, ) -> Iterator[User]: """Gets details for all the users associated with a Databricks workspace. :param attributes: str (optional) Comma-separated list of attributes to return in response. :param count: int (optional) Desired number of results per page. :param excluded_attributes: str (optional) Comma-separated list of attributes to exclude in response. :param filter: str (optional) Query by which the results have to be filtered. Supported operators are equals(`eq`), contains(`co`), starts with(`sw`) and not equals(`ne`). Additionally, simple expressions can be formed using logical operators - `and` and `or`. The [SCIM RFC] has more details but we currently only support simple expressions. [SCIM RFC]: https://tools.ietf.org/html/rfc7644#section-3.4.2.2 :param sort_by: str (optional) Attribute to sort the results. Multi-part paths are supported. For example, `userName`, `name.givenName`, and `emails`. :param sort_order: :class:`ListSortOrder` (optional) The order to sort the results. :param start_index: int (optional) Specifies the index of the first result. First item is number 1. :returns: Iterator over :class:`User` """ query = {} if attributes is not None: query["attributes"] = attributes if count is not None: query["count"] = count if excluded_attributes is not None: query["excludedAttributes"] = excluded_attributes if filter is not None: query["filter"] = filter if sort_by is not None: query["sortBy"] = sort_by if sort_order is not None: query["sortOrder"] = sort_order.value if start_index is not None: query["startIndex"] = start_index headers = { "Accept": "application/json", } # deduplicate items that may have been added during iteration seen = set() query["startIndex"] = 1 if "count" not in query: query["count"] = 10000 while True: json = self._api.do("GET", "/api/2.0/preview/scim/v2/Users", query=query, headers=headers) if "Resources" in json: for v in json["Resources"]: i = v["id"] if i in seen: continue seen.add(i) yield User.from_dict(v) if "Resources" not in json or not json["Resources"]: return query["startIndex"] += len(json["Resources"]) def patch(self, id: str, *, operations: Optional[List[Patch]] = None, schemas: Optional[List[PatchSchema]] = None): """Partially updates a user resource by applying the supplied operations on specific user attributes. :param id: str Unique ID in the Databricks workspace. :param operations: List[:class:`Patch`] (optional) :param schemas: List[:class:`PatchSchema`] (optional) The schema of the patch request. Must be ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]. """ body = {} if operations is not None: body["Operations"] = [v.as_dict() for v in operations] if schemas is not None: body["schemas"] = [v.value for v in schemas] headers = { "Content-Type": "application/json", } self._api.do("PATCH", f"/api/2.0/preview/scim/v2/Users/{id}", body=body, headers=headers) def set_permissions( self, *, access_control_list: Optional[List[PasswordAccessControlRequest]] = None ) -> PasswordPermissions: """Sets permissions on an object, replacing existing permissions if they exist. Deletes all direct permissions if none are specified. Objects can inherit permissions from their root object. :param access_control_list: List[:class:`PasswordAccessControlRequest`] (optional) :returns: :class:`PasswordPermissions` """ body = {} if access_control_list is not None: body["access_control_list"] = [v.as_dict() for v in access_control_list] headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do("PUT", "/api/2.0/permissions/authorization/passwords", body=body, headers=headers) return PasswordPermissions.from_dict(res) def update( self, id: str, *, active: Optional[bool] = None, display_name: Optional[str] = None, emails: Optional[List[ComplexValue]] = None, entitlements: Optional[List[ComplexValue]] = None, external_id: Optional[str] = None, groups: Optional[List[ComplexValue]] = None, name: Optional[Name] = None, roles: Optional[List[ComplexValue]] = None, schemas: Optional[List[UserSchema]] = None, user_name: Optional[str] = None, ): """Replaces a user's information with the data supplied in request. :param id: str Databricks user ID. :param active: bool (optional) If this user is active :param display_name: str (optional) String that represents a concatenation of given and family names. For example `John Smith`. This field cannot be updated through the Workspace SCIM APIs when [identity federation is enabled]. Use Account SCIM APIs to update `displayName`. [identity federation is enabled]: https://docs.databricks.com/administration-guide/users-groups/best-practices.html#enable-identity-federation :param emails: List[:class:`ComplexValue`] (optional) All the emails associated with the Databricks user. :param entitlements: List[:class:`ComplexValue`] (optional) Entitlements assigned to the user. See [assigning entitlements] for a full list of supported values. [assigning entitlements]: https://docs.databricks.com/administration-guide/users-groups/index.html#assigning-entitlements :param external_id: str (optional) External ID is not currently supported. It is reserved for future use. :param groups: List[:class:`ComplexValue`] (optional) :param name: :class:`Name` (optional) :param roles: List[:class:`ComplexValue`] (optional) Corresponds to AWS instance profile/arn role. :param schemas: List[:class:`UserSchema`] (optional) The schema of the user. :param user_name: str (optional) Email address of the Databricks user. """ body = {} if active is not None: body["active"] = active if display_name is not None: body["displayName"] = display_name if emails is not None: body["emails"] = [v.as_dict() for v in emails] if entitlements is not None: body["entitlements"] = [v.as_dict() for v in entitlements] if external_id is not None: body["externalId"] = external_id if groups is not None: body["groups"] = [v.as_dict() for v in groups] if name is not None: body["name"] = name.as_dict() if roles is not None: body["roles"] = [v.as_dict() for v in roles] if schemas is not None: body["schemas"] = [v.value for v in schemas] if user_name is not None: body["userName"] = user_name headers = { "Content-Type": "application/json", } self._api.do("PUT", f"/api/2.0/preview/scim/v2/Users/{id}", body=body, headers=headers) def update_permissions( self, *, access_control_list: Optional[List[PasswordAccessControlRequest]] = None ) -> PasswordPermissions: """Updates the permissions on all passwords. Passwords can inherit permissions from their root object. :param access_control_list: List[:class:`PasswordAccessControlRequest`] (optional) :returns: :class:`PasswordPermissions` """ body = {} if access_control_list is not None: body["access_control_list"] = [v.as_dict() for v in access_control_list] headers = { "Accept": "application/json", "Content-Type": "application/json", } res = self._api.do("PATCH", "/api/2.0/permissions/authorization/passwords", body=body, headers=headers) return PasswordPermissions.from_dict(res)