Metadata-Version: 2.4
Name: snipe-auth-rbac
Version: 0.3.0
Summary: Two-layer RBAC (system + company) helpers for FastAPI / Flask / any Python backend — paired with the snipe-auth-rbac TS sibling.
Project-URL: Homepage, https://github.com/snipe-solutions/auth-rbac
Project-URL: Issues, https://github.com/snipe-solutions/auth-rbac/issues
Author: Snipe Solutions
License: MIT
Keywords: auth,fastapi,permissions,rbac,supabase
Classifier: Framework :: FastAPI
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.110; extra == 'fastapi'
Requires-Dist: starlette>=0.37; extra == 'fastapi'
Provides-Extra: supabase
Requires-Dist: supabase>=2.0; extra == 'supabase'
Description-Content-Type: text/markdown

# snipe-auth-rbac (Python)

[![PyPI](https://img.shields.io/pypi/v/snipe-auth-rbac?logo=pypi&label=pypi)](https://pypi.org/project/snipe-auth-rbac/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/snipe-solutions/auth-rbac/blob/main/LICENSE)

Python sibling of the
[`snipe-auth-rbac`](https://www.npmjs.com/package/snipe-auth-rbac)
npm package. Two-layer RBAC (system + per-company roles) for
FastAPI, Flask, or any Python backend that talks to a Supabase /
Postgres database.

The full picture — including the SQL schema, the TypeScript / React
side, and the admin UI building blocks — lives in the
[top-level repo](https://github.com/snipe-solutions/auth-rbac#readme).

## Install

```bash
pip install "snipe-auth-rbac[fastapi]"
```

Optional extras:

| Extra | What it pulls in |
|---|---|
| `fastapi` | `fastapi`, `starlette` — needed for `auth_rbac.deps` |
| `supabase` | `supabase-py` |
| `dev` | `ruff`, `pytest` |

## Apply the SQL once

The tables and resolver functions live in a dedicated `rbac`
schema. The simplest path on Supabase is the bundled npm CLI (a
plain Node script — no JS knowledge needed):

```bash
npx snipe-auth-rbac install
supabase db push
# Then in Studio → Settings → API → Exposed schemas → add `rbac`
```

For other Postgres tooling, the canonical SQL files live at
[`sql/0001_initial.sql`](https://github.com/snipe-solutions/auth-rbac/blob/main/sql/0001_initial.sql)
and `0002_seed_defaults.sql` in the repo.

## FastAPI integration

```python
from fastapi import FastAPI, Depends
from auth_rbac import PermissionsService, ResourceDescriptor
from auth_rbac.deps import (
    configure_auth_rbac,
    active_company_middleware,
    require_permission,
    require_system_permission,
)
from app.integrations.supabase import get_supabase_client

RESOURCES = [
    ResourceDescriptor("system_audit", "system",  "Audit-Log",      group="Plattform"),
    ResourceDescriptor("properties",   "company", "Liegenschaften", group="Stammdaten"),
    ResourceDescriptor("payments",     "company", "Zahlungen",      group="Finanzen"),
]

app = FastAPI()
service = PermissionsService(supabase=get_supabase_client(), resources=RESOURCES)

# Fail loudly on boot if the migration hasn't been applied or
# `rbac` isn't in the project's PostgREST exposed-schemas list.
service.require_schema()

# Upsert the typed registry into rbac.resources on every boot.
# Adding a new entry to RESOURCES + redeploying makes the matrix
# UI grow a new column — no manual migration step.
service.sync_resources()

async def resolve_user_id(request):
    return request.state.user_id   # adapter to your existing auth dep

configure_auth_rbac(service=service, user_id_resolver=resolve_user_id)
app.middleware("http")(active_company_middleware)

@app.get("/payments")
async def list_payments(_=Depends(require_permission("payments", "read"))):
    ...

@app.post("/system/companies")
async def create_company(
    _=Depends(require_system_permission("system_companies", "write")),
):
    ...
```

## Other backends

`auth_rbac.deps` is FastAPI-specific, but `PermissionsService`
itself is plain Python. For Flask / Litestar / starlette-bare:

```python
from auth_rbac import PermissionsService

# Where you'd normally check role:
allowed = service.user_can(
    user_id=current_user.id,
    resource="payments",
    action="update",
    company_id=request.state.company_id,
)
if not allowed:
    abort(403)
```

## Hydrating the full profile

For a `/me/profile` endpoint that returns every role + permission
the user holds in one round-trip:

```python
profile = service.hydrate_profile(user_id)
# profile.system_roles, profile.system_permissions,
# profile.memberships -> [CompanyMembership(roles, permissions, …)]
```

The result is cached for 30 seconds per user. Swap
`_ProfileCache` for a Redis implementation if you need
cross-process invalidation.

## Pre-filling templates

```python
service.apply_template_defaults(role_id="…", only_missing=True)
# Reads default_permissions JSONB on the role and inserts
# rbac.role_permissions rows for every resource that matches.
```

## License

MIT.
