Создавайте привилегированных пользователей с Flask-HTTPAuth путем создания цепочки декораторов, теряя контекст?

Я пытаюсь создать двухуровневую систему аутентификации с базовой аутентификацией, используя Flask-HTTPAuth. Мое приложение имеет два маршрута, базовый маршрут в / доступный для любого вошедшего в систему пользователя, и маршрут администратора на /admin доступно только пользователям, которые (как и следовало ожидать) вошли в систему как администраторы.

Поэтому я решил реализовать это с помощью цепочки декораторов с соответствующей частью кода следующим образом (где dbops - это просто пространство имен, которое обрабатывает обращение к базе данных):

@auth.verify_password
def verify_pw(lastname, password):
    ln = lastname.lower()
    if ln in dbops.list_users():
        hashed_pw = dbops.find_hashed_password(ln)
        return bcrypt.checkpw(password.encode('utf8'), hashed_pw.encode('utf8'))
    return False

def must_be_admin(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        if dbops.is_admin(auth.username()):
            return f(*args, **kwargs)
        return "Not authorized."
    return wrapper

@core.route("/")
@auth.login_required
def dataentry():
    return render_template("dataentry.html")

@core.route("/admin")
@must_be_admin
@auth.login_required
def admin():
    return render_template("admin.html")

Это работает нормально, если кто-то пытается войти в систему как пользователь с правами администратора. / route: запрашивает имя пользователя и пароль, а затем пользователь с правами администратора может перейти к /admin и выполнять вход в систему задач администратора.

Тем не менее, если администратор впервые посещает /admin это не дает приглашение к входу в систему. Это просто бросает, и после того, как ковыряться в отладчике, я определил, что auth.username() возвращает пустую строку. Итак, я предполагаю, что по какой-то причине внутренний декоратор не применяется, отсюда и отсутствие запроса на вход.

Кто-нибудь знает, что здесь может происходить?

Моя первая гипотеза состояла в том, что это была простая ошибка, потому что внутренняя функция в администраторе-декораторе вызывалась только после is_admin проверять. Поэтому я попытался исправить это, вызвав функцию - и, следовательно, auth.username() доступно --- до проверки, следующим образом:

def must_be_admin(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        dummy_to_get_username = f(*args, **kwargs)
        if dbops.is_admin(auth.username()):
            return dummy_to_get_username
        return "Not authorized."
    return wrapper

Но это просто вызвало такое же поведение.

Из этого предыдущего SO я вижу, что рекомендуемый способ сделать это от автора библиотеки - просто создать два отдельных объекта Flask-HTTPAuth. Что я могу сделать, нет проблем. Но очевидно, что моя ментальная модель работы декораторов не работает, поэтому я бы хотел решить эту проблему независимо от того, какую функциональность я хочу работать...

1 ответ

Решение

Правильный порядок, в котором вы применяете декораторы, иногда трудно определить, не зная, что делает декоратор, но, к сожалению, неправильный порядок заставит приложение вести себя некорректно.

Для декораторов, которые делают что-то "до", запускается функция представления, как в этом случае, как правило, вы должны располагать декораторы в том порядке, в котором вы хотите, чтобы они выполнялись. Поэтому я думаю, что ваш код будет делать то, что вы ожидаете, когда вы используете Flask-HTTPAuth's login_required до вашего must_be_admin:

@core.route("/admin")
@auth.login_required
@must_be_admin
def admin():
    return render_template("admin.html")

Таким образом, сначала проверяются учетные данные, а в случае их отсутствия или недействительности login_required вернет браузеру ошибку 401, после чего появится приглашение для входа в систему. Только после того, как учетные данные определены как действительные, вы хотите оценить администратора декоратора.

Другие вопросы по тегам