Skip to content
Merged
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
9 changes: 3 additions & 6 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ on:
env:
VAULT_ADDR: https://vault.eng.aserto.com/
PYTHON_VERSION: "3.9"
POETRY_VERSION: "1.8.3"
TOPAZ_VERSION: "0.32.38"
POETRY_VERSION: "2.1.1"
TOPAZ_VERSION: "0.32.56"

jobs:
test:
Expand Down Expand Up @@ -119,10 +119,7 @@ jobs:
poetry publish
-
name: Bump version
id: bump
uses: callowayproject/bump-my-version@master
with:
args: patch
run: poetry version patch
-
name: Commit changes
uses: EndBug/add-and-commit@v9
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ dist
__pycache__
.DS_Store
.env
.envrc
.mypy_cache
.pytest_cache
.vscode
.coverage
.python-version
.ext
.dmypy.json
520 changes: 274 additions & 246 deletions poetry.lock

Large diffs are not rendered by default.

24 changes: 16 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "aserto"
version = "0.32.1"
version = "0.32.2"
description = "Aserto API client"
readme = "README.md"
authors = ["Aserto, Inc. <pypi@aserto.com>"]
Expand Down Expand Up @@ -31,18 +31,16 @@ packages = [
[tool.poetry.dependencies]
python = "^3.9"
aiohttp = "^3.10.2"
grpcio = "^1.64.1"
protobuf = "^5.27.2"
aserto-authorizer = "^0.20.3"
aserto-directory = "^0.33.5"
certifi = ">=2024.8.30"
aserto-directory = "^0.33.8"
aserto-authorizer = "^0.20.7"

[tool.poetry.group.dev.dependencies]
black = "^24.0"
isort= "^5.9.0"
black = "^25.1.0"
isort = "^6.0.1"
pytest-asyncio = "^0.23"
pyright = "^1.1.0"
requests = "^2.31.0"
grpc-stubs = ">=1.53.0.5"

[tool.black]
line-length = 100
Expand All @@ -51,6 +49,16 @@ target-version = ["py38"]
[tool.isort]
profile = "black"

[tool.pylint]
max-line-length = 100
disable = [
"missing-module-docstring",
"missing-class-docstring",
"missing-function-docstring",
"too-many-arguments",
"too-many-positional-arguments",
"too-many-public-methods",
]

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
5 changes: 0 additions & 5 deletions src/aserto/client/_typing.py

This file was deleted.

42 changes: 24 additions & 18 deletions src/aserto/client/authorizer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import datetime
import typing

import aserto.authorizer.v2 as authorizer
import aserto.authorizer.v2.api as api
import grpc
import grpc.aio as grpcaio

import aserto.authorizer.v2 as authorizer
from aserto.authorizer.v2 import (
CompileResponse,
GetPolicyResponse,
ListPoliciesResponse,
QueryOptions,
QueryResponse,
)

from aserto.authorizer.v2 import api
from aserto.authorizer.v2.api import IdentityContext, IdentityType

import aserto.client._deadline as timeout
import aserto.client.authorizer.helpers as helpers
import aserto.client.resource_context as res_ctx
from aserto.client.authorizer import helpers
from aserto.client.authorizer.helpers import DecisionTree
from aserto.client.identity import Identity
from aserto.client.options import AuthorizerOptions
from aserto.client.resource_context import ResourceContext

if typing.TYPE_CHECKING:
Metadata = grpc.Metadata
else:
Metadata = typing.NewType("Metadata", typing.Tuple)


class AuthorizerClient:
def __init__(
Expand All @@ -48,16 +54,16 @@ def _headers(self) -> typing.Mapping[str, str]:
return self._options.auth_headers

@property
def _metadata(self) -> grpcaio.Metadata:
return grpcaio.Metadata(*tuple(self._headers.items()))
def _metadata(self) -> Metadata:
return tuple(self._headers.items())

def decision_tree(
self,
*,
policy_path_root: str,
decisions: typing.Sequence[str],
policy_instance_name: typing.Optional[str] = None,
policy_instance_label: typing.Optional[str] = None,
policy_instance_name: str = "",
policy_instance_label: str = "",
resource_context: typing.Optional[ResourceContext] = None,
policy_path_separator: typing.Optional[typing.Literal["DOT", "SLASH"]] = None,
deadline: typing.Optional[typing.Union[datetime.datetime, datetime.timedelta]] = None,
Expand Down Expand Up @@ -93,8 +99,8 @@ def decisions(
*,
policy_path: str,
decisions: typing.Sequence[str],
policy_instance_name: typing.Optional[str],
policy_instance_label: typing.Optional[str] = None,
policy_instance_name: str = "",
policy_instance_label: str = "",
resource_context: typing.Optional[ResourceContext] = None,
deadline: typing.Optional[typing.Union[datetime.datetime, datetime.timedelta]] = None,
) -> typing.Dict[str, bool]:
Expand Down Expand Up @@ -129,8 +135,8 @@ def query(
input: str,
policy_path: str,
decisions: typing.Sequence[str],
policy_instance_name: typing.Optional[str],
policy_instance_label: typing.Optional[str] = None,
policy_instance_name: str = "",
policy_instance_label: str = "",
resource_context: typing.Optional[ResourceContext] = None,
options: typing.Optional[QueryOptions] = None,
deadline: typing.Optional[typing.Union[datetime.datetime, datetime.timedelta]] = None,
Expand Down Expand Up @@ -168,8 +174,8 @@ def compile(
disable_inlining: typing.Sequence[str],
policy_path: str,
decisions: typing.Sequence[str],
policy_instance_name: typing.Optional[str],
policy_instance_label: typing.Optional[str] = None,
policy_instance_name: str,
policy_instance_label: str = "",
resource_context: typing.Optional[ResourceContext] = None,
options: typing.Optional[QueryOptions] = None,
deadline: typing.Optional[typing.Union[datetime.datetime, datetime.timedelta]] = None,
Expand Down Expand Up @@ -203,8 +209,8 @@ def compile(
def list_policies(
self,
*,
policy_instance_name: typing.Optional[str],
policy_instance_label: typing.Optional[str] = None,
policy_instance_name: str,
policy_instance_label: str = "",
deadline: typing.Optional[typing.Union[datetime.datetime, datetime.timedelta]] = None,
) -> ListPoliciesResponse:
response = self.client.ListPolicies(
Expand All @@ -226,8 +232,8 @@ def get_policy(
self,
*,
id: str,
policy_instance_name: typing.Optional[str],
policy_instance_label: typing.Optional[str] = None,
policy_instance_name: str,
policy_instance_label: str = "",
deadline: typing.Optional[typing.Union[datetime.datetime, datetime.timedelta]] = None,
) -> GetPolicyResponse:
response = self.client.GetPolicy(
Expand Down
53 changes: 33 additions & 20 deletions src/aserto/client/authorizer/aio/__init__.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
import datetime
import typing

import aserto.authorizer.v2 as authorizer
import aserto.authorizer.v2.api as api
import grpc.aio as grpc
from grpc import ssl_channel_credentials

import aserto.authorizer.v2 as authorizer
from aserto.authorizer.v2 import (
CompileResponse,
GetPolicyResponse,
ListPoliciesResponse,
QueryOptions,
QueryResponse,
)

import aserto.authorizer.v2.api as api
from aserto.authorizer.v2.api import IdentityContext, IdentityType
from grpc import ssl_channel_credentials

import aserto.client._deadline as timeout
import aserto.client.authorizer.helpers as helpers
import aserto.client.resource_context as res_ctx
from aserto.client.authorizer import helpers
from aserto.client.authorizer.helpers import DecisionTree
from aserto.client.identity import Identity
from aserto.client.options import AuthorizerOptions
from aserto.client.resource_context import ResourceContext

if typing.TYPE_CHECKING:
AuthorizerAsyncStub = authorizer.AuthorizerAsyncStub
else:
AuthorizerAsyncStub = authorizer.AuthorizerStub


class AuthorizerClient:
def __init__(
Expand All @@ -41,7 +48,7 @@ def __init__(
target=self._options.url,
credentials=ssl_channel_credentials(self._options.cert),
)
self.client = authorizer.AuthorizerStub(self._channel)
self.client = typing.cast(AuthorizerAsyncStub, authorizer.AuthorizerStub(self._channel))

@property
def _headers(self) -> typing.Mapping[str, str]:
Expand All @@ -56,8 +63,8 @@ async def decision_tree(
*,
policy_path_root: str,
decisions: typing.Sequence[str],
policy_instance_name: typing.Optional[str] = None,
policy_instance_label: typing.Optional[str] = None,
policy_instance_name: str = "",
policy_instance_label: str = "",
resource_context: typing.Optional[ResourceContext] = None,
policy_path_separator: typing.Optional[typing.Literal["DOT", "SLASH"]] = None,
deadline: typing.Optional[typing.Union[datetime.datetime, datetime.timedelta]] = None,
Expand Down Expand Up @@ -93,8 +100,8 @@ async def decisions(
*,
policy_path: str,
decisions: typing.Sequence[str],
policy_instance_name: typing.Optional[str],
policy_instance_label: typing.Optional[str] = None,
policy_instance_name: str = "",
policy_instance_label: str = "",
resource_context: typing.Optional[ResourceContext] = None,
deadline: typing.Optional[typing.Union[datetime.datetime, datetime.timedelta]] = None,
) -> typing.Dict[str, bool]:
Expand Down Expand Up @@ -129,8 +136,8 @@ async def query(
input: str,
policy_path: str,
decisions: typing.Sequence[str],
policy_instance_name: typing.Optional[str],
policy_instance_label: typing.Optional[str] = None,
policy_instance_name: str = "",
policy_instance_label: str = "",
resource_context: typing.Optional[ResourceContext] = None,
options: typing.Optional[QueryOptions] = None,
deadline: typing.Optional[typing.Union[datetime.datetime, datetime.timedelta]] = None,
Expand Down Expand Up @@ -168,8 +175,8 @@ async def compile(
disable_inlining: typing.Sequence[str],
policy_path: str,
decisions: typing.Sequence[str],
policy_instance_name: typing.Optional[str],
policy_instance_label: typing.Optional[str] = None,
policy_instance_name: str = "",
policy_instance_label: str = "",
resource_context: typing.Optional[ResourceContext] = None,
options: typing.Optional[QueryOptions] = None,
deadline: typing.Optional[typing.Union[datetime.datetime, datetime.timedelta]] = None,
Expand Down Expand Up @@ -203,8 +210,8 @@ async def compile(
async def list_policies(
self,
*,
policy_instance_name: typing.Optional[str],
policy_instance_label: typing.Optional[str] = None,
policy_instance_name: str = "",
policy_instance_label: str = "",
deadline: typing.Optional[typing.Union[datetime.datetime, datetime.timedelta]] = None,
) -> ListPoliciesResponse:
response = await self.client.ListPolicies(
Expand All @@ -226,8 +233,8 @@ async def get_policy(
self,
*,
id: str,
policy_instance_name: typing.Optional[str],
policy_instance_label: typing.Optional[str] = None,
policy_instance_name: str = "",
policy_instance_label: str = "",
deadline: typing.Optional[typing.Union[datetime.datetime, datetime.timedelta]] = None,
) -> GetPolicyResponse:
return await self.client.GetPolicy(
Expand All @@ -244,7 +251,13 @@ async def get_policy(
),
)

async def close(self) -> None:
"""Closes the gRPC channel"""
async def close(self, grace: typing.Optional[float] = None) -> None:
"""Closes the authorizer client's connection to the server.

await self._channel.close()
If a grace period is specified, this method waits until all active
requests are finished or until the grace period is reached. Requests that haven't
been terminated within the grace period are aborted.
If a grace period is not specified (by passing None for grace),
all existing requests are cancelled immediately.
"""
await self._channel.close(grace)
12 changes: 6 additions & 6 deletions src/aserto/client/authorizer/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

from aserto.authorizer.v2 import DecisionTreeResponse, PathSeparator

from aserto.client._typing import assert_unreachable

DecisionTree = Dict[str, Dict[str, bool]]


def policy_path_separator_field(policy_path_separator: Literal["DOT", "SLASH"]) -> PathSeparator:
def policy_path_separator_field(
policy_path_separator: Literal["DOT", "SLASH"],
) -> PathSeparator.ValueType:
if policy_path_separator == "DOT":
return PathSeparator.PATH_SEPARATOR_DOT
elif policy_path_separator == "SLASH":
if policy_path_separator == "SLASH":
return PathSeparator.PATH_SEPARATOR_SLASH
else:
assert_unreachable(policy_path_separator)

raise ValueError(f"Invalid PathSeparator: {policy_path_separator}")


def validate_decision_tree(response: DecisionTreeResponse) -> DecisionTree:
Expand Down
13 changes: 13 additions & 0 deletions src/aserto/client/directory/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from typing import Literal, Tuple

from grpc import RpcError, StatusCode

from aserto.client.directory.channels import Channels

__all__ = ["Channels"]
Expand All @@ -9,6 +11,10 @@ class NotFoundError(Exception):
pass


class InvalidArgumentError(Exception):
pass


class ConfigError(Exception):
pass

Expand All @@ -20,3 +26,10 @@ def get_metadata(api_key, tenant_id) -> Tuple[Tuple[str, str], ...]:
if tenant_id:
md += (("aserto-tenant-id", tenant_id),)
return md


def translate_rpc_error(err: RpcError) -> None:
if err.code() == StatusCode.NOT_FOUND:
raise NotFoundError(err.details()) from err
if err.code() == StatusCode.INVALID_ARGUMENT:
raise InvalidArgumentError(err.details()) from err
Loading