# Buggy code
def access_validation_factory(app, validator, *args):
    """
    Creates an access validation wrapper using the supplied validator.

    :param validator: the access validator to use inside the validation wrapper
    :return: an access validator taking a request as parameter and performing the request validation
    """

    def f(request):
        """
        Creates a custom wsgi and Flask request context in order to be able to process user information
        stored in the current session.

        :param request: The Tornado request for which to create the environment and context
        """
        import flask

        wsgi_environ = WsgiInputContainer.environ(request)
        with app.request_context(wsgi_environ):
            app.session_interface.open_session(app, flask.request)
            app.login_manager.reload_user()
            validator(flask.request, *args)

    return f


# Traceback (AttributeError)
2020-06-11 22:08:49,879 - tornado.application - ERROR - Uncaught exception GET /downloads/timelapse/circle_20200611141803-fail.mpg (10.125.0.1)
Traceback (most recent call last):
File "/home/bzed/tools/OctoPrint/venv/lib/python3.7/site-packages/tornado/web.py", line 1590, in _execute
result = method(*self.path_args, **self.path_kwargs)
File "/home/bzed/tools/OctoPrint/src/octoprint/server/util/tornado.py", line 1006, in get
self._access_validation(self.request)
File "/home/bzed/tools/OctoPrint/src/octoprint/server/util/tornado.py", line 1305, in f
validator(request)
File "/home/bzed/tools/OctoPrint/src/octoprint/server/util/tornado.py", line 1285, in f
app.login_manager.reload_user()
AttributeError: 'LoginManager' object has no attribute 'reload_user'

# Fixed code
def access_validation_factory(app, validator, *args):
    """
    Creates an access validation wrapper using the supplied validator.

    :param validator: the access validator to use inside the validation wrapper
    :return: an access validator taking a request as parameter and performing the request validation
    """

    # noinspection PyProtectedMember
    def f(request):
        """
        Creates a custom wsgi and Flask request context in order to be able to process user information
        stored in the current session.

        :param request: The Tornado request for which to create the environment and context
        """
        import flask

        wsgi_environ = WsgiInputContainer.environ(request)
        with app.request_context(wsgi_environ):
            session = app.session_interface.open_session(app, flask.request)
            user_id = session.get("user_id")
            user = None

            # Yes, using protected methods is ugly. But these used to be publicly available in former versions
            # of flask-login, there are no replacements, and seeing them renamed & hidden in a minor version release
            # without any mention in the changelog means the public API ain't strictly stable either, so we might
            # as well make our life easier here and just use them...
            if user_id is not None and app.login_manager._user_callback is not None:
                user = app.login_manager._user_callback(user_id)
            app.login_manager._update_request_context_with_user(user)

            validator(flask.request, *args)

    return f