Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Authzee Logo

Authzee Python SDK

This is the official python SDK for Authzee! It is a general usage SDK that is async, extensible, and scalable.

Authzee is a highly expressive grant-based authorization engine. Check out the Authzee Repo for the core engine and specification.

Installation

$ pip install authzee

Extra dependencies can be installed like

pip install authzee[jmespath,sql-storage]

Extra dependencies available/needed:

  • jmespath - needed if using the built in jmespath execute functions
  • sql-storage - needed for SQLStorage class
  • dev - development dependencies
  • all - for all extra dependencies except for dev

Tutorial

This is the simple tutorial covering all of the main pieces to start using Authzee locally.

Simple Example

import json
from uuid import uuid4

from authzee import Authzee, DictStorage, InProcessCompute, jmespath_execute
# for asyncio use
# from authzee import AuthzeeAsync


storage_dict = {}
authz = Authzee( # for asyncio use AuthzeeAsync
    execute=jmespath_execute,
    compute_type=InProcessCompute,
    compute_kwargs={},
    storage_type=DictStorage,
    storage_kwargs={
        "storage_dict": storage_dict
    },
    config={  # optional - AuthzeeConfigOverride | None - All root and nested keys are optional
        "authzee": {
            "raise_errors": True
        }
        # "method_name": {<method config>}
    }
)
authz.construct() # one time setup for life of storage and compute
authz.start() # initialize the authzee app - must be run once for every instance
authz.put_context_def( # Context is used to pass structured data to authorization requests. Register them first as context definitions.
    {
        "context_type": "NONE", # unique
        "schema": { # JSON Schema
            "type": "object",
            "additionalProperties": False
        }
    }
)
authz.put_identity_def( # identities describe who is being authorized. Register them first as identity definitions.
    identity_def={
        "identity_type": "user", # unique
        "schema": { # JSON Schema
            "type":"object",
            "required": [
                "username",
                "department"
            ],
            "additionalProperties": False,
            "properties": {
                "username": {
                    "type": "string"
                },
                "department": {
                    "type": "string"
                }
            }
        }
    }
)
authz.put_resource_def( # resources define resource types and actions that can be taken on those resources.  Register them first as resource definitions.
    resource_def={
        "resource_type": "balloon", # unique
        "actions": [ # can be shared between resource types
            "balloon:read",
            "balloon:inflate",
            "balloon:pop"
        ],
        "schema": { # JSON Schema
            "type": "object",
            "required": [
                "color",
                "is_inflated"
            ],
            "additionalProperties": False,
            "properties": {
                "color": {
                    "type": "string"
                },
                "is_inflated": {
                    "type": "boolean"
                }
            }
        }
    }
)
authz.enact( # Enact grants to create authorization rules
    grant={
        "grant_uuid": str(uuid4()),
        "name": "Allow inflate for balloon department", # not unique
        "description": "Balloon department people are allowed to read and inflate all balloons.",
        "tags": {}, # tags for categorizing grants
        "effect": "allow", # allow or deny
        "actions": [ # list of actions to match
            "balloon:read",
            "balloon:inflate"
        ],
        "query": "length(request.identities.user[?department == 'Balloon Dept']) > `0`", # JSON Query for the request. JMESPath is preferred
        # query runs on {"request": <request>, "grant": <grant>}
        "equality": True, # expected result of the query
        "applicable_on_failure": False, # if True, grant is applicable even when query fails
        "data": {} # data available to this grant
    }
)
result = authz.authorize(
    { # request for authorization runs on:
        "identities": { # identities
            "user": [ # identity_type with array of instances
                {
                    "username": "balloon_person",
                    "department": "Balloon Dept"
                }
            ]
        },
        "action": "balloon:inflate",
        "resource_type": "balloon",
        "resource": {
            "color": "inflated",
            "is_inflated": False
        },
        "context_type": "NONE",
        "context": {}
    }
)
print(json.dumps(result, indent=4))

Authorization response:

{
    "is_authorized": true,
    "grant": {
        "grant_uuid": "49d5398a-cd5e-4944-bdb9-6543d061a53e",
        "name": "Allow inflate for balloon department",
        "description": "Balloon department people are allowed to read and inflate all balloons.",
        "tags": {},
        "effect": "allow",
        "actions": [
            "balloon:read",
            "balloon:inflate"
        ],
        "query": "length(request.identities.user[?department == 'Balloon Dept']) > `0`",
        "equality": true,
        "applicable_on_failure": false,
        "data": {}
    },
    "message": "An allow grant is applicable to the request, and there are no deny grants that are applicable to the request. Therefore, the request is authorized.",
    "error": null
}

Authzee App

The Authzee class is the entrypoint to all authzee functionality. AuthzeeAsync is available for asyncio — it has the same interface as Authzee except all methods are async.

You can check which version of the authzee specification the SDK implements via authzee.authzee_specification_version.

from authzee import (
    Authzee, 
    DictStorage,
    InProcessCompute,
    jmespath_execute, 
    paginator,
    authzee_specification_version
)
# for asyncio use AuthzeeAsync - same interface, all methods are async (use await)
# from authzee import AuthzeeAsync, paginator_async
# to include custom JMESPath functions use
# from authzee import jmespath_custom_execute

print(f"Authzee Specification Version: {authzee_specification_version}")

storage_dict = {}
authz = Authzee( # for asyncio use AuthzeeAsync
    execute=jmespath_execute, # for custom JMESPath functions use jmespath_custom_execute 
    compute_type=InProcessCompute, # compute backend type
    compute_kwargs={},
    storage_type=DictStorage, # storage backend type
    storage_kwargs={
        "storage_dict": storage_dict
    },
    config={  # optional - AuthzeeConfigOverride | None - All keys are optional
        "authzee": {
            "raise_errors": True
        }
        # "method_name": {<method config>}
    }
)
authz.construct() # one time setup for life of storage and compute
authz.start() # initialize the authzee app - must be run once for every instance

The Authzee class requires a JSON query function, compute module, and storage module.

Execute Function

The execute function is a wrapper around your choice of JSON query language. Out of the box, the SDK has:

  • jmespath_execute
    • Standard JMESPath python implementation
    • Must install authzee[jmespath]
  • jmespath_custom_execute
    • Standard JMESPath python implementation plus extra functions as outlined in the SDK recommendations
    • Must install authzee[jmespath]

If you want to create your own, see jmespath_execute for a simple example.

Compute and Storage Modules

The compute is used to process authorization requests. Storage is used to store grants and definitions. The Authzee SDK includes several of these out of the box.

Built in Compute Modules include:

  • InProcessCompute - Compute is all done within the same process/asyncio event loop.

Built in Storage Modules include:

  • DictStorage - Storage is in main memory within a python dict.

Context

Context is structured data that is passed in with authorization requests. Context types are registered in authzee with context definitions.

authz.put_context_def( # Context is used to pass structured data to authorization requests. Register them first as context definitions.
    {
        "context_type": "NONE", # unique
        "schema": { # JSON Schema
            "type": "object",
            "additionalProperties": False
        }
    }
)

Identity

Identities define who is being authorized. They are registered with authzee via identity definitions.

authz.put_identity_def( # identities describe who is being authorized. Register them first as identity definitions.
    identity_def={
        "identity_type": "user", # unique
        "schema": { # JSON Schema
            "type":"object",
            "required": [
                "username",
                "department"
            ],
            "additionalProperties": False,
            "properties": {
                "username": {
                    "type": "string"
                },
                "department": {
                    "type": "string"
                }
            }
        }
    }
)

Resource

Resources are structured data to represents your resources and actions that can be taken on them. They are registered with Authzee as resource definitions.

authz.put_resource_def( # resources define resource types and actions that can be taken on those resources.  Register them first as resource definitions.
    resource_def={
        "resource_type": "balloon", # unique
        "actions": [ # can be shared between resource types
            "balloon:read",
            "balloon:inflate",
            "balloon:pop"
        ],
        "schema": { # JSON Schema
            "type": "object",
            "required": [
                "color",
                "is_inflated"
            ],
            "additionalProperties": False,
            "properties": {
                "color": {
                    "type": "string"
                },
                "is_inflated": {
                    "type": "boolean"
                }
            }
        }
    }
)

Grant

Grants are how authorization rules are represented in Authzee.

Grants are enacted in Authzee ie a new authorization rule is added. Grants apply to any request that has a matching resource action.

authz.enact( # Enact grants to create authorization rules
    grant={
        "grant_uuid": str(uuid4()),
        "name": "Allow inflate for balloon department", # not unique
        "description": "Balloon department people are allowed to read and inflate all balloons.",
        "tags": {}, # tags for categorizing grants
        "effect": "allow", # allow or deny
        "actions": [ # list of actions to match
            "balloon:read",
            "balloon:inflate"
        ],
        "query": "length(request.identities.user[?department == 'Balloon Dept']) > `0`", # JSON Query for the request. JMESPath is preferred
        # query runs on {"request": <request>, "grant": <grant>}
        "equality": True, # expected result of the query
        "applicable_on_failure": False, # if True, grant is applicable even when query fails
        "data": {} # data available to this grant
    }
)

Authorize

Authorization is one of the core operations (or ops) of an authorization engine and brings all of the pieces together.

result = authz.authorize(
    { # request for authorization runs on:
        "identities": { # identities
            "user": [ # identity_type with array of instances
                {
                    "username": "balloon_person",
                    "department": "Balloon Dept"
                }
            ]
        },
        "action": "balloon:inflate",
        "resource_type": "balloon",
        "resource": {
            "color": "inflated",
            "is_inflated": False
        },
        "context_type": "NONE",
        "context": {}
    }
)
print(json.dumps(result, indent=4))

Authorization response:

{
    "is_authorized": true,
    "grant": {
        "grant_uuid": "49d5398a-cd5e-4944-bdb9-6543d061a53e",
        "name": "Allow inflate for balloon department",
        "description": "Balloon department people are allowed to read and inflate all balloons.",
        "tags": {},
        "effect": "allow",
        "actions": [
            "balloon:read",
            "balloon:inflate"
        ],
        "query": "length(request.identities.user[?department == 'Balloon Dept']) > `0`",
        "equality": true,
        "applicable_on_failure": false,
        "data": {}
    },
    "message": "An allow grant is applicable to the request, and there are no deny grants that are applicable to the request. Therefore, the request is authorized.",
    "error": null
}

For authorization, Authzee evaluates the given request against each grant.

How this works is the grant query, in this case JMESpath, is run on the combined data structure.

In the case of the above grant and request it would run on this data:

{
    "request": { 
        "identities": { 
            "user": [ 
                {
                    "username": "balloon_person",
                    "department": "Balloon Dept"
                }
            ]
        },
        "action": "balloon:inflate",
        "resource_type": "balloon",
        "resource": {
            "color": "inflated",
            "is_inflated": false
        },
        "context_type": "NONE",
        "context": {}
    },
    "grant": {
        "grant_uuid": "49d5398a-cd5e-4944-bdb9-6543d061a53e",
        "name": "Allow inflate for balloon department",
        "description": "Balloon department people are allowed to read and inflate all balloons.",
        "tags": {},
        "effect": "allow",
        "actions": [
            "balloon:read",
            "balloon:inflate"
        ],
        "query": "length(request.identities.user[?department == 'Balloon Dept']) > `0`",
        "equality": true,
        "applicable_on_failure": false,
        "data": {}
    }
}

The query:

length(request.identities.user[?department == 'Balloon Dept']) > `0`

Will filter all user identities by those that are in the 'Balloon Dept'. If there are more than 0 users like that, then the result of the query will be true. The expected value for the grant to be considered applicable, the equality is true. Since the result and equality are equal the grant is considered applicable to the request and the grant effect will be applied.

For authorization, by default, no requests are allowed. If a grant with the deny effect is applicable, the request is denied, no matter any other outcomes. If a grant with the allow effect is applicable, and there are no deny grants applicable then the request is allowed.

Full Example

For a more comprehensive example that demonstrates all Authzee methods, see full_example.py.

Development

Install all dependencies

pip install -e .[dev,all]

Use nox for the most common setups.

Compute and Storage Module Development

The compute and storage modules are meant to be that - modular!

You should be able to build custom ones based off of the base classes ComputeModule and StorageModule. Note that all underlying methods must be async.

Module Caching

Caching for validating a request or batch request should be self contained within the compute model per request. Besides that, it is up to the storage module to control caching for storage calls.

Release files for authzee 0.1.0a5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for authzee 0.1.0a5
File Size Uploaded
authzee-0.1.0a5.tar.gz 54.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for authzee 0.1.0a5
File Interpreter ABI Platform
authzee-0.1.0a5-py3-none-any.whl Python 3 none any Details

Total release size: 112.5 kB

Release files / authzee-0.1.0a5.tar.gz

Download URL authzee-0.1.0a5.tar.gz
Size 54.4 kB
Tags Source
SHA-256 checksum
How to use checksums
88a46f1f16dfbb178cf10542734d5bda126d94dccce60c4bdd2eb50a9f4bee8c
BLAKE2b-256 checksum
How to use checksums
127e9edec75a68ca4515a2778679d3c05b3194b49004a7e3ca79e329b342a782
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release files / authzee-0.1.0a5-py3-none-any.whl

Download URL authzee-0.1.0a5-py3-none-any.whl
Size 58.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f788d397dca9e29925e31098a1be169bd149c09d1e44f0dd4760f704a6c44bd5
BLAKE2b-256 checksum
How to use checksums
87b6f88f76fb6ce83c70b6bb0b011f8cd691750367a4b27cbecab5a959e0d8f8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page