Giter Club home page Giter Club logo

authzed-py's Introduction

Authzed Python Client

PyPI License Build Status Mailing List Discord Server Twitter

This repository houses the Python client library for Authzed.

Authzed is a database and service that stores, computes, and validates your application's permissions.

Developers create a schema that models their permissions requirements and use a client library, such as this one, to apply the schema to the database, insert data into the database, and query the data to efficiently check permissions in their applications.

Supported client API versions:

You can find more info on each API on the Authzed API reference documentation. Additionally, Protobuf API documentation can be found on the Buf Registry Authzed API repository.

See CONTRIBUTING.md for instructions on how to contribute and perform common tasks like building the project and running tests.

Getting Started

We highly recommend following the Protecting Your First App guide to learn the latest best practice to integrate an application with Authzed.

If you're interested in examples of a specific version of the API, they can be found in their respective folders in the examples directory.

Basic Usage

Installation

This project is packaged as the wheel authzed on the Python Package Index.

If you are using pip, the command to install the library is:

pip install authzed

Initializing a client

With the exception of gRPC utility functions found in grpcutil, everything required to connect and make API calls is located in a module respective to API version.

In order to successfully connect, you will have to provide a Bearer Token with your own API Token from the Authzed dashboard in place of t_your_token_here_1234567deadbeef in the following example:

from authzed.api.v1 import Client
from grpcutil import bearer_token_credentials


client = Client(
    "grpc.authzed.com:443",
    bearer_token_credentials("t_your_token_here_1234567deadbeef"),
)

Performing an API call

from authzed.api.v1 import (
    CheckPermissionRequest,
    CheckPermissionResponse,
    ObjectReference,
    SubjectReference,
)


post_one = ObjectReference(object_type="blog/post", object_id="1")
emilia = SubjectReference(object=ObjectReference(
    object_type="blog/user",
    object_id="emilia",
))

# Is Emilia in the set of users that can read post #1?
resp = client.CheckPermission(CheckPermissionRequest(
    resource=post_one,
    permission="reader",
    subject=emilia,
))
assert resp.permissionship == CheckPermissionResponse.PERMISSIONSHIP_HAS_PERMISSION

authzed-py's People

Contributors

authzedbot avatar dependabot[bot] avatar intentionally-left-nil avatar jakedt avatar jakul avatar jbergler avatar josephschorr avatar jzelinskie avatar mateenkasim avatar samkim avatar tstirrat15 avatar vroldanbet avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

authzed-py's Issues

Unable to use the attributes from the gRPC Response

Hi there, I am trying to use this with the spicedb serverless product.

I am able to create policies, etc. but when I try to run a CheckPermissionsRequest (which I know works), it doesnt allow me to access the permissionship attribute, as indicated in your examples. What am I possibly doing wrong?

resp = client.CheckPermission(
    CheckPermissionRequest(
        resource=company_ref,
        permission="administrator",
        subject=blah,
    ),
)

print(resp.permissionship)

Wrap gRPC Exceptions

Depending on whether or not the client is async, it returns entirely different exception types: RpcError and AioRpcError respectively.

The grpcutil module should have an exception type that can be used to catch both of these and handle them equivalently.

Allow newer versions of `protobuf`

Hello, this project pins currently restricts protobuf to v20 with a 3.20.3 constraint.
We'd like to use v21 or v22 in our project alongside this package.

There are some minor breaking changes noted in the v21 release for python here

They don't look particularly major, but I'm not sure if any of those notes are of interest to this project.

An update to the dependency would be greatly appreciated.

Getting Protobuf error on 0.15.0 with python 3.11.9 in docker container

Setup

I'm running a fastapi API using uvicorn on Python 3.11.9 in a linux/amd64 docker container with an alpine Linux image.

container image: audavisaiacr.azurecr.io/customer-configuration-service:latest to be precise

with python3.11.9 installed via apk

Symptoms

Running the latest 0.15.0 authzed-py version with protobuf 5.26. or 5.27 results in error on import Client

File "/config_service/app/main.py", line 3, in <module>
    from app.routes.user import router as user_router
  File "/config_service/app/routes/user.py", line 7, in <module>
    from app.dependencies import (
  File "/config_service/app/dependencies.py", line 3, in <module>
    from authzed.api.v1 import Client
  File "/usr/local/lib/python3.11/site-packages/authzed/api/v1/__init__.py", line 18, in <module>
    from authzed.api.v1.experimental_service_pb2 import (
  File "/usr/local/lib/python3.11/site-packages/authzed/api/v1/experimental_service_pb2.py", line 15, in <module>
    from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2
  File "/usr/local/lib/python3.11/site-packages/google/api/annotations_pb2.py", line 44, in <module>
    google_dot_protobuf_dot_descriptor__pb2.MethodOptions.RegisterExtension(http)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: type object 'MethodOptions' has no attribute 'RegisterExtension'

It works on MacOS directly with 0.15.0 and 3.11.9

It works with 0.14.0 and Python 3.11.9 within the same docker container.

I'm not sure how to proceed to fix this.

Expose experimental services in Client

At the moment SpiceDB Client doesn't have ExperimentalServiceStub or WatchServiceStub added. This means that users need to roll their own copy of Client with them added.

It would be great to add them to Client or ExperimentalClient if the API is unstable enough that you would need to opt-in into it.

Client initialization throws an error if run in a sub thread

In the client initialization it is run the following line to decide whether to run grpc async or not.

if asyncio.get_event_loop().is_running()

Unfortunately this the asyncio.get_event_loop() throws the following error if run in a sub-thread.

RuntimeError: There is no current event loop in thread 'Thread-2'.

Also, the get_event_loop function creates an event loop if it does not find any, and this is not a great thing if run in a synchronous environment. Moreover in the asyncio documentation is said that get_event_loop is deprecated since python 3.10 (see https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.get_event_loop), and they suggest using get_running_loop instead.

My environment

  • python 3.9.9
  • authzed 0.4.2

How to reproduce

The following code is a fast way to reproduce the problem

import threading
from authzed.api.v1 import Client
from grpcutil import insecure_bearer_token_credentials

def instantiate_client():
    Client("localhost:50051", insecure_bearer_token_credentials("some-token"))

th = threading.Thread(target=instantiate_client)
th.start()

Possible solution

  • create two different classes, Client and AsyncClient.
  • change how channelfn is set with something like:
try:
    asyncio.get_running_loop()
    channelfn = grpc.aio.secure_channel
except RuntimeError:
    channelfn = grpc.secure_channel

Failed to connect to all addresses; Endpoint is neither UDS or TCP loopback address

Hi, I am receiving this error when connecting to the spicedb client. The code only works when I am using localhost:50051 as the client address. Any help will be appreciated please ๐Ÿ™‚

Traceback (most recent call last):
  File "/examples/write_schemas.py", line 20, in <module>
    resp = client.WriteSchema(WriteSchemaRequest(schema=SCHEMA))
  File "/usr/local/lib/python3.8/site-packages/grpc/_channel.py", line 1030, in __call__
    return _end_unary_response_blocking(state, call, False, None)
  File "/usr/local/lib/python3.8/site-packages/grpc/_channel.py", line 910, in _end_unary_response_blocking
    raise _InactiveRpcError(state)  # pytype: disable=not-instantiable
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
       status = StatusCode.UNAVAILABLE
       details = "failed to connect to all addresses; last error: UNKNOWN: ipv4:172.21.0.3:50051: Endpoint is neither UDS or TCP loopback address."
       debug_error_string = "UNKNOWN:failed to connect to all addresses; last error: UNKNOWN: ipv4:172.21.0.3:50051: Endpoint is neither UDS or TCP loopback address. {grpc_status:14, created_time:"2023-06-23T09:18:19.7666023+00:00"}"

write_schemas.py - https://github.com/authzed/authzed-py/tree/main/examples/v1

from authzed.api.v1 import Client
from authzed.api.v1 import WriteSchemaRequest
from grpcutil import insecure_bearer_token_credentials

SCHEMA = """definition blog/user {}

definition blog/post {
    relation reader: blog/user
    relation writer: blog/user

    permission read = reader + writer
    permission write = writer
}"""

client = Client(
    "spicedb:50051", # this does not work, change to localhost:50051 to work
    insecure_bearer_token_credentials("presharedkey"),
)

resp = client.WriteSchema(WriteSchemaRequest(schema=SCHEMA))

docker-compose.yml

version: "3"

x-spicedb-env: &spicedb-env
  environment:
    SPICEDB_DATASTORE_ENGINE: postgres
    SPICEDB_DATASTORE_CONN_URI: postgres://postgres:password@database:5432/spicedb
    SPICEDB_GRPC_PRESHARED_KEY: presharedkey

services:
  api:
    image: python:3.8
    volumes:
      - ./write_schemas.py:/examples/write_schemas.py
    command: bash -c "pip install authzed==0.9.0 && python3 /examples/write_schemas.py"
    networks:
      - spicedb

  database:
    image: postgres:14.3
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: spicedb
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - spicedb

  spicedb:
    image: authzed/spicedb:v1.20.0
    command: serve
    <<: *spicedb-env
    ports:
      - 50051:50051
    networks:
      - spicedb

  migrate:
    image: authzed/spicedb:v1.20.0
    command: migrate head
    <<: *spicedb-env
    networks:
      - spicedb

networks:
  spicedb:
    driver: bridge

Steps to reproduce

  1. Create write_schemas.py and docker-compose.yml
  2. Run the following commands
docker-compose up -d database
docker-compose up migrate
docker-compose up -d spicedb
docker-compose up api

Bump upper bound of protobuf dependencies

protobuf v4.25 came out last month, so it'd be awesome if this client allowed v25 too. Not interested in updating the lockfile, just bumping the upper bound as was done in my fork here.

Let me know if you'd like me to make a PR or if you'd rather do some testing beforehand. It works as expected in my own small use case, which resolved the protobuf version to 4.25.1 due to another package's stricter requirements (>=4.25.0).

Protobuf/Grpc version policy

As this lib is made to be used in many components there's a high chance that it is to be embedded in a project already relying on protobuf.

The version pinning is quite strict at this time (~4.21) and no options were left for people with a protobuf stack pre v21 but to use older release of the lib.

For the context I'm working on implementing a fine grained authz system thanks to SpiceDB in a quite vast project leveraging tensorflow and having a lib with such strict version pinning and fast pace makes me uncomfortable.

As gRPC guarantees forward compatibility up to the next major included (for non-experimental/deprecated features) and protobuf offers forward compatibility up to the next major excluded, I would expect wider ranges.

Can you give us the details of your policy regarding this matter ?

Wild thought : as generated pb_py files are tied to a protobuf version, making a separated lib holding these files with its own protobuf version requirement would allow this lib to use a wider range of protobuf while still having version-specific files already compiled.

Passing SubjectReference into resource leads to Segmentation fault

Current Authzed Python library doesn't feel very Pythonic and as a consequence it's sometimes in the beginning hard to figure out when it needs SubjectReference vs ObjectReference.

What I found puzzling is that passing wrong Reference type wouldn't raise an Exception but instead crash whole Python:

Python 3.11.4 (main, Jun 7 2023, 12:45:48) [GCC 11.3.0] on linux on arm64:

Test with:

from authzed.api.v1 import Client
from grpcutil import bearer_token_credentials

client = Client(
    "grpc.authzed.com:443",
    bearer_token_credentials("t_your_token_here_1234567deadbeef"),
)

SCHEMA = """definition blog/user {}

definition blog/post {
    relation reader: blog/user
    relation writer: blog/user

    permission read = reader + writer
    permission write = writer
}"""
resp = client.WriteSchema(WriteSchemaRequest(schema=SCHEMA))

resp = client.WriteRelationships(
    WriteRelationshipsRequest(
        updates=[
            # Emilia is a Writer on Post 1
            RelationshipUpdate(
                operation=RelationshipUpdate.Operation.OPERATION_CREATE,
                relationship=Relationship(
# this resource= should be ObjectReference, but developer error made it SubjectReference
                    resource=SubjectReference(object=ObjectReference(object_type="blog/post", object_id="1")),
                    relation="writer",
                    subject=SubjectReference(
                        object=ObjectReference(
                            object_type="blog/user",
                            object_id="emilia",
                        )
                    ),
                ),
            )]
))

brew spicedb and python client

I am following the steps listed in the readme:

  1. Get the server running locally
    spicedb serve --grpc-preshared-key "somerandomkeyhere" --grpc-no-tls

  2. Using a python client i try to query the server

client = Client("localhost:50053",     insecure_bearer_token_credentials("somerandomkeyhere"))
resp = client.CheckPermission(CheckPermissionRequest(
    resource=post_one,
    permission="reader",
    subject=emilia,
))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/anagpal/auth/venv/lib/python3.8/site-packages/grpc/_channel.py", line 946, in __call__
    return _end_unary_response_blocking(state, call, False, None)
  File "/Users/anagpal/auth/venv/lib/python3.8/site-packages/grpc/_channel.py", line 849, in _end_unary_response_blocking
    raise _InactiveRpcError(state)
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
	status = StatusCode.UNIMPLEMENTED
	details = "unknown service authzed.api.v1.PermissionsService"
	debug_error_string = "{"created":"@1633729479.125902000","description":"Error received from peer ipv6:[::1]:50053","file":"src/core/lib/surface/call.cc","file_line":1070,"grpc_message":"unknown service authzed.api.v1.PermissionsService","grpc_status":12}"

I am guessing the formula needs to be updated?

Getting <_MultiThreadedRendezvous object> in LookupResourcesRequest

I'm trying to get list of resources particular subject has access to.. but when I run my python script I get <_MultiThreadedRendezvous object> response. Not sure what to do with that.

Here's my code:

org_owner = ObjectReference(object_type="user", object_id="org_owner")
org_owner_s = SubjectReference(object=org_owner)
resp = client.LookupResources(
    LookupResourcesRequest(
        # resource=record["resource"],
        resource_object_type="files",
        permission="read",
        subject=org_owner_s,
    )
)
print(resp)

can anyone please help me here?

Examine using python/betterproto as the generating plugin

I ran across this when I was trying to figure out how to install plugins: https://github.com/danielgtaylor/python-betterproto

It seems to generate more comprehensible, idiomatic code, which can be good for devs looking to the code to understand the behavior of the library.

The current hurdle is that we use client streaming and there isn't a released version of the lib that supports it. I think there's also some concern about whether the lib is maintained - I've seen several people asking for a release and no response from the maintainer. Gonna monitor this.

Add type hint support

The authzed library already ships with most of the necessary type hints in *.pyi files. However, mypy can't use this because:

  1. There's no py.typed file in the package so these types can't be used at all
  2. Even if the file were there, there's no types for __init__.py
  3. In __init__.py the type for the Client is wrong/ really hard to implement because it could either be the Async* versions or the Sync versions

E.g. depending on whether you're in an async loop, the correct definition is either

class Client(SchemaServiceStub, PermissionsServiceStub, ExperimentalServiceStub, WatchServiceStub):
  ...

OR

class Client(SchemaServiceAsyncStub, PermissionsServiceAsyncStub, ExperimentalServiceAsyncStub, WatchServiceAsyncStub):
  ...

So, largely we need to solve the __init__.py file before we can continue. I have one concrete suggestion: Make a separate AsyncClient which has the latter type signature. Then, make a new SyncClient which is just an alias to the existing Client. That will allow the type hints to be straightforward.

Then, if we add the type hints directly to the __init__.py file for the init method, we wouldn't need a separate __init__.pyi file since the init file would have all of the necessary type hints

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.