FastAPI decorators on endpoint
Question:
I’m trying to create role-based access control on endpoint and since fastAPI has this build-in Depends method with possibility to cache result I’m trying to create something like this
@router.get('/')
#decorator
@roles_decorator("admin")
async def get_items(user_id: str = Depends(get_current_user)):
return await get_all_items()
get_current_user method will need to receive roles from decorator (in this case admin) and from authorization service receive user_id if role matches provided role. So my question is, can we pass the role from decorator to method in Depends, or is there any chance that we can do this role-based access control with predefined roles for every endpoint?
def get_current_user(role):
#connect to auth_serivce and do other logic
return user_id
Answers:
Instead of trying to mix dependencies and decorators (which won’t do anything good), you can instead use a dynamically configured dependency:
async def get_current_user_with_role(role):
async def get_user_and_validate(user=Depends(get_current_user)):
if not user.has_role(role):
raise 403
return user.id
return get_user_and_validate
This will bind the role
value you give to the dependency when it gets created:
@router.get('/')
async def get_items(user_id: str = Depends(get_current_user_with_role("admin"))):
pass
I’m new to Python and FastAPI but passing params to a dependency wasn’t working too well for me, and it started causing the params to appear in Swagger which wasn’t what I wanted.
My endpoints require API keys for authentication, so I check who the API key belongs to and if they have the required permissions to access the endpoint.
Coming from other frameworks where I’d use a decorator for this kind of thing, I ended up implementing it this way in my FastAPI project:
Here’s the decorator:
def has_permission(permission: str):
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
api_key = kwargs.get("api_key_header")
user_service = kwargs.get("user_service")
user = user_service.get_by_api_key(api_key)
if permission not in user.permissions
raise HTTPException(status_code=403, detail="User doesn't have required permissions")
return await func(*args, **kwargs)
return wrapper
return decorator
And this is how I use it on my route:
@router.get("/", summary="Retrieve all users.", response_model=List[UserResponse])
@has_permission("users.get")
async def get_all_users(
api_key_header: str = Security(api_key_header), user_service: UserService = Depends(UserService)
):
"""
Retrieve all users.
"""
return user_service.get_all_users()
I’m trying to create role-based access control on endpoint and since fastAPI has this build-in Depends method with possibility to cache result I’m trying to create something like this
@router.get('/')
#decorator
@roles_decorator("admin")
async def get_items(user_id: str = Depends(get_current_user)):
return await get_all_items()
get_current_user method will need to receive roles from decorator (in this case admin) and from authorization service receive user_id if role matches provided role. So my question is, can we pass the role from decorator to method in Depends, or is there any chance that we can do this role-based access control with predefined roles for every endpoint?
def get_current_user(role):
#connect to auth_serivce and do other logic
return user_id
Instead of trying to mix dependencies and decorators (which won’t do anything good), you can instead use a dynamically configured dependency:
async def get_current_user_with_role(role):
async def get_user_and_validate(user=Depends(get_current_user)):
if not user.has_role(role):
raise 403
return user.id
return get_user_and_validate
This will bind the role
value you give to the dependency when it gets created:
@router.get('/')
async def get_items(user_id: str = Depends(get_current_user_with_role("admin"))):
pass
I’m new to Python and FastAPI but passing params to a dependency wasn’t working too well for me, and it started causing the params to appear in Swagger which wasn’t what I wanted.
My endpoints require API keys for authentication, so I check who the API key belongs to and if they have the required permissions to access the endpoint.
Coming from other frameworks where I’d use a decorator for this kind of thing, I ended up implementing it this way in my FastAPI project:
Here’s the decorator:
def has_permission(permission: str):
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
api_key = kwargs.get("api_key_header")
user_service = kwargs.get("user_service")
user = user_service.get_by_api_key(api_key)
if permission not in user.permissions
raise HTTPException(status_code=403, detail="User doesn't have required permissions")
return await func(*args, **kwargs)
return wrapper
return decorator
And this is how I use it on my route:
@router.get("/", summary="Retrieve all users.", response_model=List[UserResponse])
@has_permission("users.get")
async def get_all_users(
api_key_header: str = Security(api_key_header), user_service: UserService = Depends(UserService)
):
"""
Retrieve all users.
"""
return user_service.get_all_users()