Skip to content

Commit 7b9b0a8

Browse files
committed
Improve and test tornado.web.authenticated heuristic
1 parent 0facfec commit 7b9b0a8

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

jupyter_server/serverapp.py

+20-2
Original file line numberDiff line numberDiff line change
@@ -538,15 +538,33 @@ def _check_handler_auth(
538538
method = getattr(handler, method_name.lower())
539539
is_unimplemented = method == web.RequestHandler._unimplemented_method
540540
is_allowlisted = hasattr(method, "__allow_unauthenticated")
541-
# TODO: modify `tornado.web.authenticated` upstream?
542-
is_blocklisted = method.__code__.co_qualname.startswith("authenticated")
541+
is_blocklisted = _has_tornado_web_authenticated(method)
543542
if not is_unimplemented and not is_allowlisted and not is_blocklisted:
544543
missing_authentication.append(
545544
f"- {method_name} of {handler.__name__} registered for {matcher}"
546545
)
547546
return missing_authentication
548547

549548

549+
def _has_tornado_web_authenticated(method: t.Callable) -> bool:
550+
"""Check if given method was decorated with @web.authenticated.
551+
552+
Note: it is ok if we reject on @authorized @web.authenticated
553+
because the correct order is @web.authenticated @authorized.
554+
"""
555+
if not hasattr(method, "__wrapped__"):
556+
return False
557+
if not hasattr(method, "__code__"):
558+
return False
559+
code = method.__code__
560+
if hasattr(code, "co_qualname"):
561+
# new in 3.11
562+
return code.co_qualname.startswith("authenticated")
563+
elif hasattr(code, "co_filename"):
564+
return code.co_filename.replace("\\", "/").endswith("tornado/web.py")
565+
return False
566+
567+
550568
class JupyterPasswordApp(JupyterApp):
551569
"""Set a password for the Jupyter server.
552570

tests/test_serverapp.py

+22
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,19 @@
99

1010
import pytest
1111
from jupyter_core.application import NoStart
12+
from tornado import web
1213
from traitlets import TraitError
1314
from traitlets.config import Config
1415
from traitlets.tests.utils import check_help_all_output
1516

17+
from jupyter_server.auth.decorator import allow_unauthenticated, authorized
1618
from jupyter_server.auth.security import passwd_check
1719
from jupyter_server.serverapp import (
1820
JupyterPasswordApp,
1921
JupyterServerListApp,
2022
ServerApp,
2123
ServerWebApplication,
24+
_has_tornado_web_authenticated,
2225
list_running_servers,
2326
random_ports,
2427
)
@@ -637,3 +640,22 @@ def test_immutable_cache_trait():
637640
serverapp.init_configurables()
638641
serverapp.init_webapp()
639642
assert serverapp.web_app.settings["static_immutable_cache"] == ["/test/immutable"]
643+
644+
645+
def test():
646+
pass
647+
648+
649+
@pytest.mark.parametrize(
650+
"method, expected",
651+
[
652+
[test, False],
653+
[allow_unauthenticated(test), False],
654+
[authorized(test), False],
655+
[web.authenticated(test), True],
656+
[web.authenticated(authorized(test)), True],
657+
[authorized(web.authenticated(test)), False], # wrong order!
658+
],
659+
)
660+
def test_tornado_authentication_detection(method, expected):
661+
assert _has_tornado_web_authenticated(method) == expected

0 commit comments

Comments
 (0)