Web framework adapters¶
RBACX ships simple adapters for popular frameworks. for popular Python web frameworks. They all follow the same conventions:
- EnvBuilder builds
(Subject, Action, Resource, Context)from the framework request/scope. - Async frameworks use async adapters (
evaluate_asyncunder the hood). - Sync frameworks use sync adapters (
evaluate_sync). - By default, do not leak reasons. If you need diagnostics, pass
add_headers=Trueand read: X-RBACX-ReasonX-RBACX-RuleX-RBACX-Policy
See runnable apps in examples/.
FastAPI (dependency)¶
from fastapi import FastAPI, Depends, Request
from rbacx import Guard, Subject, Resource, Action, Context
from rbacx.adapters.fastapi import require_access
policy = {"algorithm": "deny-overrides", "rules": [
{"id": "doc_read", "effect": "permit", "actions": ["read"], "resource": {"type": "doc"}}
]}
guard = Guard(policy)
def build_env(request: Request):
uid = request.headers.get("x-user", "anonymous")
return Subject(id=uid, roles=["user"]), Action("read"), Resource(type="doc"), Context()
app = FastAPI()
@app.get("/doc", dependencies=[Depends(require_access(guard, build_env, add_headers=True))])
async def doc():
return {"ok": True}
Flask (decorator)¶
from flask import Flask, request
from rbacx import Guard, Subject, Resource, Action, Context
from rbacx.adapters.flask import require_access
guard = Guard(policy)
def build_env(req):
# use explicit req or implicit flask.request
r = req or request
uid = r.headers.get("x-user", "anonymous")
return Subject(id=uid, roles=["user"]), Action("read"), Resource(type="doc"), Context()
app = Flask(__name__)
@app.get("/doc")
@require_access(guard, build_env, add_headers=True)
def doc():
return {"ok": True}
Starlette (decorator)¶
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from rbacx import Guard, Subject, Resource, Action, Context
from rbacx.adapters.starlette import require_access
guard = Guard(policy)
def build_env(request: Request):
uid = request.headers.get("x-user", "anonymous")
return Subject(id=uid, roles=["user"]), Action("read"), Resource(type="doc"), Context()
app = Starlette()
@app.route("/doc")
@require_access(guard, build_env, add_headers=True)
async def doc(request: Request):
return JSONResponse({"ok": True})
Litestar (middleware)¶
from litestar import Litestar, get
from litestar.middleware import DefineMiddleware
from rbacx import Guard, Subject, Resource, Action, Context
from rbacx.adapters.litestar import RBACXMiddleware
guard = Guard(policy)
def build_env(scope):
uid = dict(scope.get("headers", [])).get(b"x-user", b"anonymous").decode("latin1")
return Subject(id=uid, roles=["user"]), Action("read"), Resource(type="doc"), Context()
@get("/doc")
async def doc() -> dict:
return {"ok": True}
app = Litestar(
route_handlers=[doc],
middleware=[DefineMiddleware(RBACXMiddleware, guard=guard, build_env=build_env, add_headers=True)],
)
Django (decorator + middleware)¶
Enable the middleware to inject a Guard:
# settings.py
RBACX_GUARD_FACTORY = "rbacx_demo.rbacx_factory.build_guard"
MIDDLEWARE = [
# ...
"rbacx.adapters.django.middleware.RbacxDjangoMiddleware",
]
Use the decorator:
from rbacx import Subject, Resource, Action, Context
from rbacx.adapters.django.decorators import require_access
def build_env(request):
uid = getattr(getattr(request, "user", None), "id", None) or "anonymous"
return Subject(id=str(uid), roles=["user"]), Action("read"), Resource(type="doc"), Context()
@require_access(build_env, add_headers=True)
def doc(request):
...
Django REST Framework (permission)¶
from rest_framework.views import APIView
from rest_framework.response import Response
from rbacx import Guard, Subject, Resource, Action, Context
from rbacx.adapters.drf import make_permission
guard = Guard(policy)
def build_env(request):
uid = getattr(getattr(request, "user", None), "username", None) or "anonymous"
return Subject(id=uid, roles=["user"]), Action("read"), Resource(type="doc"), Context()
RBACXPermission = make_permission(guard, build_env, add_headers=True)
# Optionally attach headers on 403:
# REST_FRAMEWORK = {"EXCEPTION_HANDLER": "rbacx.adapters.drf.rbacx_exception_handler"}
class DocsView(APIView):
permission_classes = [RBACXPermission]
def get(self, request):
return Response({"ok": True})