Embedded SQLAlchemy 2.0-native authorization library that converts declarative Python policies into SQL WHERE clauses.
Project description
sqla-authz
Declarative authorization that compiles to SQL WHERE clauses.
An embedded, open-source, SQLAlchemy 2.0-native authorization library. Define policies in Python, get SQL filters automatically -- no external servers, no network round-trips, no custom DSLs.
Fills the vacuum left by Oso's deprecation (December 2023). No maintained alternative exists that generates SQL query filters for SQLAlchemy 2.0.
Why sqla-authz?
| Feature | sqla-authz | sqlalchemy-oso | PyCasbin | Cerbos |
|---|---|---|---|---|
| SQL WHERE clause generation | Yes | Yes (deprecated) | No | Yes (via server) |
SQLAlchemy 2.0 (select()) |
Yes | No | N/A | Yes |
| AsyncSession | Yes | No | N/A | No |
| Embedded (no server) | Yes | Yes | Yes | No |
| Python-native policies | Yes | No (Polar DSL) | No (.conf files) | No (YAML) |
| Type-safe (pyright strict) | Yes | No | No | No |
Quick Start
from sqla_authz import policy, authorize_query
from sqlalchemy import select, ColumnElement, or_
# 1. Define a policy
@policy(Post, "read")
def post_read_policy(actor: User) -> ColumnElement[bool]:
return or_(
Post.is_published == True,
Post.author_id == actor.id,
)
# 2. Apply authorization to any query
stmt = select(Post).order_by(Post.created_at.desc())
stmt = authorize_query(stmt, actor=current_user, action="read")
result = await session.execute(stmt)
posts = result.scalars().all()
# SQL: SELECT ... FROM post WHERE (is_published = true OR author_id = :id)
Installation
pip install sqla-authz
# With FastAPI integration
pip install sqla-authz[fastapi]
# With Flask integration
pip install sqla-authz[flask]
# With test utilities
pip install sqla-authz[testing]
# Everything
pip install sqla-authz[all]
Features
- SQLAlchemy 2.0
select()style exclusively - Both sync
SessionandAsyncSession - Relationship traversal via
has()/any()(compiles to EXISTS) - Composable predicates with
&,|,~operators - FastAPI integration via dependency injection (
AuthzDep) - Flask integration via extension (
AuthzExtension) - Point checks:
can(actor, action, resource)andauthorize(actor, action, resource) - Audit logging for authorization decisions
- Consumer test utilities (
sqla_authz.testing) - pyright strict mode compatible
- Zero runtime dependencies beyond SQLAlchemy
Architecture
graph TD
A["@policy(Post, 'read')<br/>Python function"] --> B[PolicyRegistry]
B --> C{Entry Point}
C -->|Explicit| D["authorize_query(stmt, actor, action)"]
C -->|Automatic| E["do_orm_execute event hook"]
C -->|FastAPI| F["AuthzDep(Post, 'read')"]
C -->|Flask| F2["AuthzExtension"]
D --> G[Policy Compiler]
E --> G
F --> G
F2 --> G
G --> H["ColumnElement[bool]<br/>SA filter expression"]
H --> I["stmt.where(filter)"]
I --> J["session.execute()<br/>(sync or async)"]
style A fill:#e1f5fe
style H fill:#c8e6c9
style J fill:#fff3e0
Key Design Decisions
- Pure Python policies -- no DSL, no config files. Type-safe, IDE-friendly, debuggable.
- SQL-native -- policies compile to
ColumnElement[bool]. The database does the filtering. - Explicit by default --
authorize_query()is visible and greppable. Automatic mode is opt-in. - Async-equal -- same compilation code for
SessionandAsyncSession. Filter construction is pure Python (no I/O). - Fail-closed -- missing policy = zero rows, not a data leak.
Documentation
- Technical Design Document -- Full architecture and API specification
- Research Findings -- Prior art analysis and design rationale
Contributing
# Clone and install dev dependencies
git clone https://github.com/colbyjoines/sqla-authz.git
cd sqla-authz
uv pip install -e ".[dev]"
# Run tests
pytest
# Lint and format
ruff check src/ tests/
ruff format src/ tests/
# Type check
pyright src/
Status
Phase 3: Complete. Core MVP, integrations (FastAPI, Flask), testing utilities, audit logging, and benchmarks are implemented.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sqla_authz-0.1.0b1.tar.gz.
File metadata
- Download URL: sqla_authz-0.1.0b1.tar.gz
- Upload date:
- Size: 18.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
41b8e2a2961fdc963525cfb9a0cdc7027bf0b1ba92157d3c979faceb2c0c5915
|
|
| MD5 |
1e6ae070986cf88bacf32e76494921e9
|
|
| BLAKE2b-256 |
bdb80b3a772a63e8b5258fde9d64e998e0be152ca4cd217754854afa14f158b3
|
File details
Details for the file sqla_authz-0.1.0b1-py3-none-any.whl.
File metadata
- Download URL: sqla_authz-0.1.0b1-py3-none-any.whl
- Upload date:
- Size: 32.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a2c18cf4d63c3715c2b1854b354d3f2eb21d67d3ff1e0fe061e501e36b305cb
|
|
| MD5 |
a2392ca9d3f477dd552f5fe0300c7be2
|
|
| BLAKE2b-256 |
e276c409c503f1432a3dbbd60dfe695294fbff43cd78297b758b4c419c789f60
|