Рубрики
Без рубрики

Как реализовать Управление Доступом на основе ролей С помощью Fast API

Краткое изложение концепции RBAC, рабочих фрагментов кода и того, как я туда попал

Автор оригинала: Mandar Vaze.

Что такое Управление доступом на основе ролей (RBAC)

Большинство приложений CRUD требуют определенного уровня управления доступом на основе ролей.

У вас может быть как минимум два типа пользователей.

  1. Пользователь с повышенными правами доступа (администратор, суперпользователь или суперпользователь)
  2. Обычный пользователь он же все остальные

Скорее всего, у вас есть больше уровней между ними.

Это означает, что только пользователи с определенной ролью могут получить доступ к определенным конечным точкам API или операциям, например, разрешить всем операцию GET , но только admin может УДАЛИТЬ . Некоторые промежуточные уровни могут создавать/обновлять и т. Д.

Код

Следующий код предполагает, что ваша модель User имеет атрибут role . Лучше иметь значение по умолчанию, чтобы каждый созданный пользователь начинался с самого низкого уровня, даже если роль не назначена при создании.

Давайте сначала определим класс Role Checker следующим образом:

class RoleChecker:
    def __init__(self, allowed_roles: List):
        self.allowed_roles = allowed_roles

    def __call__(self, user: User = Depends(get_current_active_user)):
        if user.role not in self.allowed_roles:
            logger.debug(f"User with role {user.role} not in {self.allowed_roles}")
            raise HTTPException(status_code=403, detail="Operation not permitted")

Затем в вашем файле маршрутов используйте его следующим образом:

allow_create_resource = RoleChecker(["admin"])

@router.post(
    "/some-resource/",
    response_model=schemas.MyResource,
    status_code=201,
    dependencies=[Depends(allow_create_resource)],
)
def add_resource(resource: schemas.ResourceCreate, db: Session = Depends(get_db)):
    # Some validation like resource does not already exist
    # Create the resource
    pass

Иногда требуется разрешить нескольким ролям выполнять определенные операции. Вот почему, RoleChecker принимает список ролей, таких как:

allow_create_resource = RoleChecker(["admin", "manager"])

Обучение (Или как я сюда попал)

Если вы пришли сюда просто в поисках решения, то можете перестать читать прямо сейчас.

Читайте дальше, чтобы узнать, как я пришел к решению, что я пробовал (и потерпел неудачу)

(Иногда такие детали дают вам представление о том, что вам может понадобиться в будущем)

Как вы, возможно, знаете, вы можете получить текущие сведения о пользователе в API с помощью инъекции зависимостей через user:(get_current_user) Смотрите документацию

Так легко первая попытка была на линии

if user.role not 'admin':
    raise HTTPException(status_code=403, detail="Operation not permitted")

Я расширил вышеизложенное до user.role not in ["admin", "manager"] , чтобы разрешить нескольким ролям выполнять эту операцию.

Это работает для “доказательства концепции”, но мы не можем добавлять подобный код везде

Затем я создал

def verify_role(required_role: List, user: User = Depends(get_current_active_user)):
    if user.role not in required_role:
        raise HTTPException(status_code=403, detail="Operation not permitted")

Мне нужно было передать список ролей функции. К сожалению, я не мог назвать это через Depends . Я продолжал получать Depends не имеет атрибута ... ошибка.

Кроме того, мне нужно вызвать это из функции router decorator как dependencies=[Dependencies(my_func)] , а не в функции param like user:(get_current_user)

Наконец другой пользователь указал мне на этот раздел документации, и это было все. 🎉

Спасибо

Я благодарен Marcelo aka Kydex и Danny Rohde on Fast API gitter за идеи и помощь.