I need to implement object permission in my application. I can handle this in each endpoint but it will lead to a lot of duplicated code.
For example:
@router.get(...)
def read_item(..., item_id: int, ...):
item = service.get_item(id=item_id)
if item.owner_id != current_user.id:
raise NotOwnerException
...
This is a simple scenario.
Is there a way to handle object permission without having to repeat code, maybe using dependencies injection?
I will need in some way to pass item_id to the dependency but I think that it's not possibile.
Has anyone found a viable solution?
I could also create a function like this:
def check_permission(item: Item, current_user: User)
...
and call this function in each endpoint, but it doesn't seem a very elegant solution.
What do you think?
@benzgrant -- Have you looked at dependencies in path decorator? A working example where visiting /items/100 returns data otherwise throws an exception:
from fastapi import Depends, FastAPI, Path, HTTPException
router = FastAPI()
def check_permission(item_id: int = Path(...)):
# depending on your auth library/process, you might do:
# user = get_current_active_user()
# note: this is an example/demo but could mirror your above code
if item_id != 100:
raise HTTPException(status_code=401, detail="Unauthorised")
# you could do a lookup here if you wanted and return the item as an object from its ID
return item_id
@router.get("/items/{item_id}", dependencies=[Depends(check_permission)])
def read_item(item_id: int):
return {"item_id": item_id}
One caveat is that using dependencies above does not allow for updating item_id inside check_permission, so if you want to modify item_id (e.g. to assign it as an item object), you could update read_item to use the Depends directly in the parameter:
@router.get("/items/{item_id}")
def read_item(item_id: int = Depends(check_permission)):
return {"item_id": item_id}
Also, the auth library that you're using (e.g. OAuth2) likely offers a way to customise the depends decorator, such as to get_current_active_user. Assuming you're using such auth workflows, you could then update the above example such that check_permission makes a call to e.g. get_current_active_user.
@jawrainey On the dependency item_id is actually from Path, not from Header. :clinking_glasses:
Thanks it's working! I'll try to build a more advance object permission system on top of your solution :)
I'll share it here as soon as I write something functional :)