Fastapi: User object permission

Created on 31 Oct 2020  路  3Comments  路  Source: tiangolo/fastapi

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?

enhancement

All 3 comments

@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 :)

Was this page helpful?
0 / 5 - 0 ratings