Добавление декораторов auth в flask restx

У меня есть приложение Flask, использующее flask-restx а также flask-login. Я бы хотел, чтобы все маршруты по умолчанию требовали входа в систему и явно определяли общедоступные маршруты, не требующие аутентификации. Я начал использовать декораторы, следуя примеру, приведенному в этом вопросе:

Лучший способ сделать значение login_required во Flask-Login по умолчанию

Он работает для конечных точек функций, но не для restx конечные точки ресурсов.

Я пробовал добавить функцию как декоратор, так и с помощью method_decoratorsполе. Например:

def public_route(decorated_function):
    """
    This is a decorator to specify public endpoints in our flask routes
    :param decorated_function:
    :return:
    """
    decorated_function.is_public = True
    return decorated_function


class HelloWorld(ConfigurableResource):

    method_decorators = {"get": [public_route]}

    @public_route
    @api.doc('Welcome message')
    def get(self):
        return {'hello': 'world'}

И этот тест проходит:

def test_hello_world_is_public():
    api = Namespace('health', description='Health related operations')
    hello = HelloWorld(api, config=None, logger=None)
    is_public_endpoint = getattr(hello.get, 'is_public', False)
    assert is_public_endpoint

Моя проблема в том, что я не вижу, как получить доступ к этому атрибуту в моей логике авторизации:


    @app.before_request
    def check_route_access():
        """
        This function decides whethere access should be granted to an endpoint.
        This function runs before all requests.
        :return:
        """
        is_public_endpoint = getattr(app.view_functions[request.endpoint], 'is_public', False)

        if person_authorized_for_path(current_user, request.path, is_public_endpoint):
            return
        # Otherwise access not granted
        return redirect(url_for("auth.index"))

Это работает для конечных точек простых функций, но не для ресурсов restx.

Я это понимаю restxоборачивает мой класс ресурсов в функцию, чтобы фляга могла выполнять отправку, но я не могу понять, как получить доступ к декоратору отсюда. Итак, у меня есть вопросы:

  • Можно ли добраться до декоратора из view_function?
  • Можно ли узнать, является ли конечная точка ресурсом restx или простой функцией отдыха?
  • Есть ли лучший способ сделать то, что я пытаюсь достичь?

3 ответа

Исходя из этого и этого,method_decorators Переменная должна быть списком функций, поэтому вы должны использовать ее так:

def _perform_auth(method):
    is_public_endpoint = getattr(method, 'is_public', False)
    # place the validation here

class Resource(flask_restx.Resource):
    method_decorators = [_perform_auth]

Можно ли добраться до декоратора из view_function?

Что ж... это возможно, но я бы не рекомендовал это. Вот пример

Можно ли узнать, является ли конечная точка ресурсом restx или простой функцией отдыха?

Вы, вероятно, можете проверить func и выяснить, не от restx, возможно, глядя на __qualname__, но опять же, я бы не рекомендовал это.

Есть ли лучший способ сделать то, что я пытаюсь достичь?

Я бы выбрал одно из этих решений:

  • Явно украшайте view_funcs и ресурсы, которые нуждаются в аутентификации, а не наоборот
  • Создайте схему общедоступных конечных точек, схему защищенных конечных точек с помощью before_request декоратор для авторизации

У меня был аналогичный вариант использования. Я хотел применить декоратор к определенным конечным точкам (здесь все маршруты, кроме методов get), используя декоратор по умолчанию flask-restx.

Это мое решение.

      def check_role_except_get(func):
    @functools.wraps(func)
    def wrapper(*args,**kwargs):
        if func.__name__ == "get"
            return func(*args,**kwargs)
        else:
              ........ Authorization Logic Here / Abort if Authorization fails----
    return wrapper


def authorization(func):
    @functools.wraps(func)
    def wrapper(*args,**kwargs):
        func.view_class.method_decorators = [check_role_except_get]
        return func(*args,**kwargs)
    return wrapper

api = Api(app, version='1.0', title='My API',
    description='A simple API',decorators=[authorization]
)
ns = api.namespace('todos', description='TODO operations')

@ns.route('/')
class TodoList(Resource):
    '''Shows a list of all todos, and lets you POST to add new tasks'''
    def get(self):
        '''List all tasks'''
        return "resp"

    def post(self):
        '''Create a new task'''
        return resp

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