From b719aec29805a657054ee4409b4062ec39c07be2 Mon Sep 17 00:00:00 2001 From: Razco Date: Tue, 9 Apr 2024 18:59:35 +0300 Subject: [PATCH] add pdp api and list role assignment api from pdp --- permit/pdp_api/__init__.py | 0 permit/pdp_api/base.py | 67 ++++++++++++++++++++++++++++ permit/pdp_api/models.py | 29 +++++++++++++ permit/pdp_api/pdp_api_client.py | 40 +++++++++++++++++ permit/pdp_api/role_assignments.py | 70 ++++++++++++++++++++++++++++++ permit/permit.py | 3 +- 6 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 permit/pdp_api/__init__.py create mode 100644 permit/pdp_api/base.py create mode 100644 permit/pdp_api/models.py create mode 100644 permit/pdp_api/pdp_api_client.py create mode 100644 permit/pdp_api/role_assignments.py diff --git a/permit/pdp_api/__init__.py b/permit/pdp_api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/permit/pdp_api/base.py b/permit/pdp_api/base.py new file mode 100644 index 0000000..6cfe529 --- /dev/null +++ b/permit/pdp_api/base.py @@ -0,0 +1,67 @@ +from typing import Callable, TypeVar + +from permit import PermitConfig, PYDANTIC_VERSION + +from permit.api.base import SimpleHttpClient + + +if PYDANTIC_VERSION < (2, 0): + from pydantic import BaseModel, Extra, Field, parse_obj_as +else: + from pydantic.v1 import BaseModel, Extra, Field, parse_obj_as # type: ignore + + + + +T = TypeVar("T", bound=Callable) +TModel = TypeVar("TModel", bound=BaseModel) +TData = TypeVar("TData", bound=BaseModel) + + + + +def pagination_params(page: int, per_page: int) -> dict: + return {"page": page, "per_page": per_page} + + +class ClientConfig(BaseModel): + class Config: + extra = Extra.allow + + base_url: str = Field( + ..., + description="base url that will prefix the url fragment sent via the client", + ) + headers: dict = Field(..., description="http headers sent to the API server") + + + + +class BasePdpPermitApi: + """ + The base class for Permit APIs. + """ + + def __init__(self, config: PermitConfig): + """ + Initialize a BasePermitApi. + + Args: + config: The Permit SDK configuration. + """ + self.config = config + + def _build_http_client(self, endpoint_url: str = "", **kwargs): + client_config = ClientConfig( + base_url=f"{self.config.pdp}", + headers={ + "Content-Type": "application/json", + "Authorization": f"bearer {self.config.token}", + }, + ) + client_config_dict = client_config.dict() + client_config_dict.update(kwargs) + return SimpleHttpClient( + client_config_dict, + base_url=endpoint_url, + ) diff --git a/permit/pdp_api/models.py b/permit/pdp_api/models.py new file mode 100644 index 0000000..1674236 --- /dev/null +++ b/permit/pdp_api/models.py @@ -0,0 +1,29 @@ +# generated by datamodel-codegen: +# filename: open.json (local PDP) +# timestamp: 2024-04-09T15:36:45+00:00 + +from __future__ import annotations + +from typing import Optional + +from ..utils.pydantic_version import PYDANTIC_VERSION + +if PYDANTIC_VERSION < (2, 0): + from pydantic import BaseModel, Field +else: + from pydantic.v1 import BaseModel, Field # type: ignore + + + +class RoleAssignment(BaseModel): + user: str = Field(..., description='the user the role is assigned to', title='User') + role: str = Field(..., description='the role that is assigned', title='Role') + tenant: str = Field( + ..., description='the tenant the role is associated with', title='Tenant' + ) + resource_instance: Optional[str] = Field( + None, + description='the resource instance the role is associated with', + title='Resource Instance', + ) + diff --git a/permit/pdp_api/pdp_api_client.py b/permit/pdp_api/pdp_api_client.py new file mode 100644 index 0000000..e5a9814 --- /dev/null +++ b/permit/pdp_api/pdp_api_client.py @@ -0,0 +1,40 @@ +from ..config import PermitConfig +from .condition_set_rules import ConditionSetRulesApi +from .condition_sets import ConditionSetsApi +from .deprecated import DeprecatedApi +from .environments import EnvironmentsApi +from .projects import ProjectsApi +from .relationship_tuples import RelationshipTuplesApi +from .resource_action_groups import ResourceActionGroupsApi +from .resource_actions import ResourceActionsApi +from .resource_attributes import ResourceAttributesApi +from .resource_instances import ResourceInstancesApi +from .resource_relations import ResourceRelationsApi +from .resource_roles import ResourceRolesApi +from .resources import ResourcesApi +from .role_assignments import RoleAssignmentsApi +from .roles import RolesApi +from .tenants import TenantsApi +from .users import UsersApi + + +class PermitApiClient(DeprecatedApi): + def __init__(self, config: PermitConfig): + """ + Constructs a new instance of the PdpApiClient class with the specified SDK configuration. + + Args: + config: The configuration for the Permit SDK. + """ + self._config = config + self._headers = { + "Content-Type": "application/json", + "Authorization": f"bearer {self._config.token}", + } + self._base_url = self._config.pdp + + self._role_assignments = RoleAssignmentsApi(config) + + @property + def role_assignments(self) -> RoleAssignmentsApi: + return self._role_assignments diff --git a/permit/pdp_api/role_assignments.py b/permit/pdp_api/role_assignments.py new file mode 100644 index 0000000..e1584fb --- /dev/null +++ b/permit/pdp_api/role_assignments.py @@ -0,0 +1,70 @@ +from typing import List, Optional + +from permit.api.base import SimpleHttpClient + +from permit import PYDANTIC_VERSION +from permit.pdp_api.base import BasePdpPermitApi, pagination_params +from permit.pdp_api.models import RoleAssignment + +if PYDANTIC_VERSION < (2, 0): + from pydantic import validate_arguments +else: + from pydantic.v1 import validate_arguments # type: ignore + + + + +class RoleAssignmentsApi(BasePdpPermitApi): + @property + def __role_assignments(self) -> SimpleHttpClient: + return self._build_http_client( + "/local/role_assignments" + ) + + + @validate_arguments + async def list( + self, + user_key: Optional[str] = None, + role_key: Optional[str] = None, + tenant_key: Optional[str] = None, + resource_key: Optional[str] = None, + resource_instance_key: Optional[str] = None, + page: int = 1, + per_page: int = 100, + ) -> List[RoleAssignment]: + """ + Retrieves a list of role assignments based on the specified filters. + + Args: + user_key: optional user filter, will only return role assignments granted to this user. + role_key: optional role filter, will only return role assignments granting this role. + tenant_key: optional tenant filter, will only return role assignments granted in that tenant. + resource_key: optional resource type filter, will only return role assignments granted on that resource type. + resource_instance_key: optional resource instance filter, will only return role assignments granted on that resource instance. + page: The page number to fetch (default: 1). + per_page: How many items to fetch per page (default: 100). + + Returns: + an array of role assignments. + + Raises: + PermitApiError: If the API returns an error HTTP status code. + PermitContextError: If the configured ApiContext does not match the required endpoint context. + """ + params = pagination_params(page, per_page) + if user_key is not None: + params.update(dict(user=user_key)) + if role_key is not None: + params.update(dict(role=role_key)) + if tenant_key is not None: + params.update(dict(tenant=tenant_key)) + if resource_key is not None: + params.update(dict(resource=resource_key)) + if resource_instance_key is not None: + params.update(dict(resource_instance=resource_instance_key)) + return await self.__role_assignments.get( + "", + model=List[RoleAssignment], + params=params, + ) diff --git a/permit/permit.py b/permit/permit.py index b811c9e..7bbb3e4 100644 --- a/permit/permit.py +++ b/permit/permit.py @@ -8,6 +8,7 @@ from .config import PermitConfig from .enforcement.enforcer import Action, CheckQuery, Enforcer, Resource, User from .logger import configure_logger +from .pdp_api.base import BasePdpPermitApi from .utils.context import Context @@ -21,7 +22,7 @@ def __init__(self, config: Optional[PermitConfig] = None, **options): self._enforcer = Enforcer(self._config) self._api = PermitApiClient(self._config) self._elements = ElementsApi(self._config) - + self. _pdp_api = BasePdpPermitApi(self._config) logger.debug( "Permit SDK initialized with config:\n${}", json.dumps(self._config.dict(exclude={"api_context"})),