Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clients/client-python/gravitino/api/authorization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@

from __future__ import annotations

from gravitino.api.authorization.group import Group
from gravitino.api.authorization.privileges import Privileges
from gravitino.api.authorization.role import Role
from gravitino.api.authorization.securable_objects import SecurableObjects
from gravitino.api.authorization.user import User

__all__ = [
"Group",
"Role",
"SecurableObjects",
"Privileges",
"User",
]
47 changes: 47 additions & 0 deletions clients/client-python/gravitino/api/authorization/group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

from __future__ import annotations

from abc import abstractmethod

from gravitino.api.auditable import Auditable


class Group(Auditable):
"""The interface of a group. The group is a collection of users in the authorization system."""

@abstractmethod
def name(self) -> str:
"""
The name of the group.

Returns:
str: The name of the group.
"""
raise NotImplementedError()

@abstractmethod
def roles(self) -> list[str]:
"""
The roles of the group. A group can have multiple roles.
Every role binds several privileges.

Returns:
list[str]: The role names of the group.
"""
raise NotImplementedError()
58 changes: 56 additions & 2 deletions clients/client-python/gravitino/api/authorization/privileges.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,5 +219,59 @@ class Condition(Enum):


class Privileges:
# TODO Implement the Privileges class.
pass
"""The helper class for Privilege. Provides factory methods to create privilege instances."""

@staticmethod
def allow(name: Privilege.Name) -> Privilege:
"""Create a Privilege with ALLOW condition.

Args:
name: The name of the privilege.

Returns:
Privilege: A privilege instance with ALLOW condition.
"""
return _GenericPrivilege(Privilege.Condition.ALLOW, name)

@staticmethod
def deny(name: Privilege.Name) -> Privilege:
"""Create a Privilege with DENY condition.

Args:
name: The name of the privilege.

Returns:
Privilege: A privilege instance with DENY condition.
"""
return _GenericPrivilege(Privilege.Condition.DENY, name)


class _GenericPrivilege(Privilege):
"""Generic implementation of Privilege that works with any Privilege.Name."""

def __init__(self, condition: Privilege.Condition, name: Privilege.Name):
self._condition = condition
self._name = name

def name(self) -> Privilege.Name:
return self._name

def simple_string(self) -> str:
return f"{self._condition.value} {self._name.name.lower().replace('_', ' ')}"

def condition(self) -> Privilege.Condition:
return self._condition

def can_bind_to(self, obj_type: MetadataObject.Type) -> bool:
return True

def __eq__(self, other: object) -> bool:
if not isinstance(other, Privilege):
return False
return self._condition == other.condition() and self._name == other.name()

def __hash__(self) -> int:
return hash((self._condition, self._name))

def __repr__(self) -> str:
return f"Privileges.{self._condition.value.lower()}(Privilege.Name.{self._name.name})"
47 changes: 47 additions & 0 deletions clients/client-python/gravitino/api/authorization/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

from __future__ import annotations

from abc import abstractmethod

from gravitino.api.auditable import Auditable


class User(Auditable):
"""The interface of a user. The user is a basic entity in the authorization system."""

@abstractmethod
def name(self) -> str:
"""
The name of the user.

Returns:
str: The name of the user.
"""
raise NotImplementedError()

@abstractmethod
def roles(self) -> list[str]:
"""
The roles of the user. A user can have multiple roles.
Every role binds several privileges.

Returns:
list[str]: The role names of the user.
"""
raise NotImplementedError()
61 changes: 61 additions & 0 deletions clients/client-python/gravitino/client/dto_converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

from gravitino.api.catalog import Catalog
from gravitino.api.catalog_change import CatalogChange
from gravitino.api.authorization.privileges import Privilege
from gravitino.api.authorization.securable_objects import SecurableObject, SecurableObjects
from gravitino.api.job.job_template import JobTemplate, JobType
from gravitino.api.job.job_template_change import (
JobTemplateChange,
Expand All @@ -36,6 +38,8 @@
from gravitino.client.fileset_catalog import FilesetCatalog
from gravitino.client.generic_model_catalog import GenericModelCatalog
from gravitino.client.relational_catalog import RelationalCatalog
from gravitino.dto.authorization.privilege_dto import PrivilegeDTO
from gravitino.dto.authorization.securable_object_dto import SecurableObjectDTO
from gravitino.dto.catalog_dto import CatalogDTO
from gravitino.dto.job.job_template_dto import JobTemplateDTO
from gravitino.dto.job.shell_job_template_dto import ShellJobTemplateDTO
Expand Down Expand Up @@ -325,3 +329,60 @@ def to_tag_update_request(
return TagUpdateRequest.RemoveTagPropertyRequest(change.removed_property)

raise IllegalArgumentException(f"Unknown change type: {type(change)}")

@staticmethod
def to_privilege_dto(privilege: Privilege) -> PrivilegeDTO:
"""Convert an API Privilege to a PrivilegeDTO for serialization.

Args:
privilege: The API Privilege object.

Returns:
PrivilegeDTO: The serializable DTO.
"""
return PrivilegeDTO(privilege.name(), privilege.condition())

@staticmethod
def from_privilege_dto(dto: PrivilegeDTO) -> Privilege:
"""Convert a PrivilegeDTO to an API Privilege.

Args:
dto: The PrivilegeDTO to convert.

Returns:
Privilege: The API Privilege object.
"""
return dto

@staticmethod
def to_securable_object_dto(obj: SecurableObject) -> SecurableObjectDTO:
"""Convert an API SecurableObject to a SecurableObjectDTO for serialization.

Args:
obj: The API SecurableObject.

Returns:
SecurableObjectDTO: The serializable DTO.
"""
privilege_dtos = [DTOConverters.to_privilege_dto(p) for p in obj.privileges()]
return SecurableObjectDTO(
obj.full_name(),
obj.type(),
privilege_dtos,
)

@staticmethod
def from_securable_object_dto(dto: SecurableObjectDTO) -> SecurableObject:
"""Convert a SecurableObjectDTO to an API SecurableObject.

Args:
dto: The SecurableObjectDTO to convert.

Returns:
SecurableObject: The API SecurableObject.
"""
return SecurableObjects.parse(
dto.full_name(),
dto.type(),
dto.privileges(),
)
82 changes: 82 additions & 0 deletions clients/client-python/gravitino/client/gravitino_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,18 @@

from typing import Dict, List, Optional

from gravitino.api.authorization.group import Group
from gravitino.api.authorization.privileges import Privilege
from gravitino.api.authorization.role import Role
from gravitino.api.authorization.securable_objects import SecurableObject
from gravitino.api.authorization.user import User
from gravitino.api.catalog import Catalog
from gravitino.api.catalog_change import CatalogChange
from gravitino.api.job.job_handle import JobHandle
from gravitino.api.job.job_template import JobTemplate
from gravitino.api.job.job_template_change import JobTemplateChange
from gravitino.api.job.supports_jobs import SupportsJobs
from gravitino.api.metadata_object import MetadataObject
from gravitino.api.tag.tag_operations import TagOperations
from gravitino.auth.auth_data_provider import AuthDataProvider
from gravitino.client.gravitino_client_base import GravitinoClientBase
Expand Down Expand Up @@ -330,3 +336,79 @@ def delete_tag(self, tag_name) -> bool:
NoSuchMetalakeException: If the metalake does not exist.
"""
return self.get_metalake().delete_tag(tag_name)

# Authorization operations

def add_user(self, user: str) -> User:
return self.get_metalake().add_user(user)

def remove_user(self, user: str) -> bool:
return self.get_metalake().remove_user(user)

def get_user(self, user: str) -> User:
return self.get_metalake().get_user(user)

def list_users(self) -> list[User]:
return self.get_metalake().list_users()

def list_user_names(self) -> list[str]:
return self.get_metalake().list_user_names()

def add_group(self, group: str) -> Group:
return self.get_metalake().add_group(group)

def remove_group(self, group: str) -> bool:
return self.get_metalake().remove_group(group)

def get_group(self, group: str) -> Group:
return self.get_metalake().get_group(group)

def list_groups(self) -> list[Group]:
return self.get_metalake().list_groups()

def list_group_names(self) -> list[str]:
return self.get_metalake().list_group_names()

def create_role(
self,
role: str,
properties: dict[str, str],
securable_objects: list[SecurableObject],
) -> Role:
return self.get_metalake().create_role(role, properties, securable_objects)

def delete_role(self, role: str) -> bool:
return self.get_metalake().delete_role(role)

def get_role(self, role: str) -> Role:
return self.get_metalake().get_role(role)

def list_role_names(self) -> list[str]:
return self.get_metalake().list_role_names()

def grant_roles_to_user(self, roles: list[str], user: str) -> User:
return self.get_metalake().grant_roles_to_user(roles, user)

def revoke_roles_from_user(self, roles: list[str], user: str) -> User:
return self.get_metalake().revoke_roles_from_user(roles, user)

def grant_roles_to_group(self, roles: list[str], group: str) -> Group:
return self.get_metalake().grant_roles_to_group(roles, group)

def revoke_roles_from_group(self, roles: list[str], group: str) -> Group:
return self.get_metalake().revoke_roles_from_group(roles, group)

def grant_privileges_to_role(
self, role: str, obj: MetadataObject, privileges: list[Privilege]
) -> Role:
return self.get_metalake().grant_privileges_to_role(role, obj, privileges)

def revoke_privileges_from_role(
self, role: str, obj: MetadataObject, privileges: list[Privilege]
) -> Role:
return self.get_metalake().revoke_privileges_from_role(role, obj, privileges)

def list_binding_role_names(
self, type_: MetadataObject.Type, full_name: str
) -> list[str]:
return self.get_metalake().list_binding_role_names(type_, full_name)
Loading