fromfastapiimportFastAPI,Request,Depends,HTTPExceptionfromtypingimportSet,Listfromfunctoolsimportwrapsapp=FastAPI()# Preserve existing public routes functionalitypublic_routes:Set[str]=set()public_static:Set[str]=set()
[docs]defpublic_route():"""Decorator to mark a route as public (no authentication required)"""defdecorator(func):@wraps(func)asyncdefwrapper(*args,**kwargs):# Mark the request as a public route for any middleware that needs to knowrequest:Request=next((argforarginargsifisinstance(arg,Request)),None)ifrequest:request.state.public_route=Truereturnawaitfunc(*args,**kwargs)# Mark the function as a public route so the startup hook can find itwrapper.__public_route__=Truereturnwrapperreturndecorator
[docs]defadd_public_static(path_start):"""Add a path prefix to the public static routes"""public_static.add(path_start)
[docs]defrequires_any_role(*roles:str):defrole_checker(request:Request):ifnotrequest.state.user:raiseHTTPException(status_code=401,detail="Not authenticated")user_roles=request.state.user.rolesifnotany(roleinuser_rolesforroleinroles):raiseHTTPException(status_code=403,detail=f"User needs one of these roles: {', '.join(roles)}")returnTruereturnDepends(role_checker)