fastapi-has-permissions
Introduction¶
Declarative permissions system for FastAPI. Define permission checks as classes or functions,
compose them with &, |, ~ operators, and plug them into FastAPI's dependency injection.
Installation¶
pip install fastapi-has-permissions
Usage¶
Class-Based Permissions¶
Subclass Permission and implement check_permissions():
from fastapi import Depends, FastAPI, Request
from fastapi_has_permissions import Permission
class HasAuthorizationHeader(Permission):
async def check_permissions(self, request: Request) -> bool:
return "Authorization" in request.headers
app = FastAPI()
@app.get(
"/protected",
dependencies=[Depends(HasAuthorizationHeader())],
)
async def protected():
return {"message": "You have access!"}
Permissions with parameters are automatically dataclasses:
class HasRole(Permission):
role: str
async def check_permissions(self, request: Request) -> bool:
return request.headers.get("role") == self.role
Boolean Composition¶
Combine permissions with & (AND), | (OR), and ~ (NOT):
# All must pass
Depends(HasAuthorizationHeader() & HasRole("admin"))
# Any must pass
Depends(HasAuthorizationHeader() | HasRole("admin"))
# Negated
Depends(~HasAuthorizationHeader())
Function-Based Permissions¶
Use the @permission decorator for a lightweight alternative:
from typing import Annotated
from fastapi import Header
from fastapi_has_permissions import permission
@permission
async def has_admin_role(role: Annotated[str, Header()]) -> bool:
return role == "admin"
@app.get("/admin", dependencies=[Depends(has_admin_role())])
async def admin_endpoint():
return {"message": "Admin access granted"}
Function-based permissions also support Dep arguments for injecting FastAPI dependencies:
from fastapi_has_permissions import Dep, permission
async def get_admin_role() -> str:
return "admin"
AdminRoleDep = Annotated[str, Depends(get_admin_role)]
@permission
async def has_role(admin_role: Dep[str], /, role: Annotated[str, Header()]) -> bool:
return role == admin_role
@app.get("/admin", dependencies=[Depends(has_role(AdminRoleDep))])
async def admin_endpoint():
return {"message": "Admin access granted"}
Function-based permissions support the same &, |, ~ composition.
Lazy Permissions¶
Defer dependency resolution to request time with lazy() - useful when dependencies
may not always be available:
from fastapi.exceptions import RequestValidationError
from fastapi_has_permissions import lazy
# Skip the check instead of failing if the "age" header is missing
Depends(lazy(AgeIsMoreThan(age=18), skip_on_exc=(RequestValidationError,)))
Other Features¶
- Custom error responses -- set
default_exc_message/default_exc_status_codeclass variables or overrideget_exc_message()/get_exc_status_code()methods - Skip / Fail helpers -- call
skip()orfail()insidecheck_permissions()for explicit control flow - Built-in common permissions --
IsAuthenticated,HasScope,HasRoleready to use with your auth dependencies - Full FastAPI DI support --
check_permissions()accepts any FastAPI-injectable parameters