Что такое Управление доступом на основе ролей (RBAC)
Большинство приложений CRUD требуют определенного уровня управления доступом на основе ролей.
У вас может быть как минимум два типа пользователей.
- Пользователь с повышенными правами доступа (администратор, суперпользователь или суперпользователь)
- Обычный пользователь он же все остальные
Скорее всего, у вас есть больше уровней между ними.
Это означает, что только пользователи с определенной ролью могут получить доступ к определенным конечным точкам 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 за идеи и помощь.