Metadata-Version: 2.4
Name: pyauthz
Version: 0.9.0
Summary: Framework-agnostic authorization for modern Python. RBAC, ABAC, PBAC — unified, typed, Pydantic-native, async-first, auditable.
Project-URL: Homepage, https://github.com/MateuszMarciszewski/pyauthz
Project-URL: Repository, https://github.com/MateuszMarciszewski/pyauthz
Project-URL: Documentation, https://github.com/MateuszMarciszewski/pyauthz
Project-URL: Changelog, https://github.com/MateuszMarciszewski/pyauthz/blob/main/CHANGELOG.md
Author: Marci
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: abac,access-control,async,audit,authentication,authorization,compliance,django,fastapi,flask,graphql,grpc,nist,permissions,policy,pydantic,rbac,security
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: pydantic>=2.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: all
Requires-Dist: django>=4.2; extra == 'all'
Requires-Dist: fastapi>=0.100; extra == 'all'
Requires-Dist: flask>=3.0; extra == 'all'
Requires-Dist: opentelemetry-api>=1.20; extra == 'all'
Requires-Dist: redis>=5.0; extra == 'all'
Requires-Dist: sqlalchemy>=2.0; extra == 'all'
Requires-Dist: strawberry-graphql>=0.300; extra == 'all'
Requires-Dist: watchfiles>=0.20; extra == 'all'
Provides-Extra: dev
Requires-Dist: hypothesis>=6.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.9; extra == 'dev'
Provides-Extra: django
Requires-Dist: django>=4.2; extra == 'django'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.100; extra == 'fastapi'
Provides-Extra: flask
Requires-Dist: flask>=3.0; extra == 'flask'
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.20; extra == 'otel'
Provides-Extra: redis
Requires-Dist: redis>=5.0; extra == 'redis'
Provides-Extra: sql
Requires-Dist: sqlalchemy>=2.0; extra == 'sql'
Provides-Extra: strawberry
Requires-Dist: strawberry-graphql>=0.300; extra == 'strawberry'
Provides-Extra: watch
Requires-Dist: watchfiles>=0.20; extra == 'watch'
Description-Content-Type: text/markdown

# PyAuthz

**Framework-agnostic authorization for modern Python.**

> *The pyauthz stands between your application and unauthorized access.*

RBAC, ABAC, and Policy-Based Access Control — unified, typed, Pydantic-native, async-first, and auditable out of the box.

## Install

```bash
pip install pyauthz
```

## 30-Second Quickstart

```python
from pyauthz import PyAuthz, Subject, Resource, Role

rp = PyAuthz()
rp.add_role(Role(name="admin", permissions=["*:*"]))

user = Subject(id="alice", roles=["admin"])
doc = Resource(id="doc-1", type="documents")

rp.is_allowed(user, "read", doc)  # True — action auto-expands to "documents:read"
```

Three lines after imports. No policies to define, no config to write. As complexity grows, add explicit policies, conditions, and YAML files.

## Why PyAuthz?

| Feature | Casbin | Oso | PyAuthz |
|---------|--------|-----|---------|
| Framework-agnostic | Yes | Yes | **Yes** |
| Pydantic/Typed | No | No | **Yes** |
| Async-first | Partial | No | **Yes** |
| Embeddable | Yes | **Deprecated** | **Yes** |
| Auditable | No | No | **Yes** |
| Maintained | Yes | **No** | **Yes** |

## Features

- **Progressive complexity** — 3 lines to start, scales to enterprise patterns
- **Pythonic** — Pydantic models, type hints, decorators. No custom DSL.
- **Async-native, sync-friendly** — async engine with synchronous wrappers
- **Auditable** — structured Decision objects with full reasoning chains
- **Policy-as-code** — Python objects and/or YAML files, Git-friendly
- **Deny by default** — closed-by-default is the secure starting point

## Define Policies

### In Python

```python
from pyauthz import Policy, Effect, Condition

admin_access = Policy(
    name="admin-full-access",
    effect=Effect.ALLOW,
    roles=["admin"],
    permissions=["*:*"],
)

own_docs_only = Policy(
    name="owner-edit-documents",
    effect=Effect.ALLOW,
    roles=["editor"],
    permissions=["documents:write", "documents:delete"],
    conditions=[
        Condition(field="resource.owner_id", op="==", value="subject.id"),
    ],
)
```

### In YAML

```yaml
# policies/rbac.yaml
version: 1

roles:
  - name: admin
    permissions: ["*:*"]
  - name: editor
    permissions: [documents:write, documents:read]

policies:
  - name: admin-full-access
    effect: allow
    roles: [admin]
    permissions: ["*:*"]
```

## Check Access

```python
from pyauthz import PyAuthz, Subject, Resource, Denied

rp = PyAuthz()
rp.load_policies("policies/")

user = Subject(id="alice", roles=["editor"], attributes={"department": "engineering"})
doc = Resource(id="doc-123", type="documents", owner_id="alice")

# Simple bool check
if rp.is_allowed(user, "write", doc):
    ...

# Decision with reasoning (raises Denied on deny)
decision = rp.authorize(user, "documents:write", doc)
print(decision.explain())

# Handle denial
try:
    rp.authorize(user, "pii:read", pii_record)
except Denied as e:
    print(e.decision.reasoning)
```

## Async Support

```python
decision = await rp.aauthorize(user, "documents:write", doc)
allowed = await rp.ais_allowed(user, "documents:write", doc)
```

## Audit Logging

```python
from pyauthz import AuditLogger, JsonExporter

audit = AuditLogger(exporters=[JsonExporter(path="logs/authz.jsonl")])
rp.set_audit_logger(audit)
# Every authorize() call now produces structured audit events
```

## Error Handling

PyAuthz follows **fail loud on load, fail safe on evaluate**:

- Invalid policies raise `SchemaError` at load time
- Missing attributes produce safe `DENY` decisions at evaluation time (never crash)
- The `Denied` exception carries the full `Decision` object

## License

Apache 2.0
