Skip to content

Commit 0b453c6

Browse files
committed
Add allow_unauthenticated_access traitlet and @allow_unauthenticated
1 parent 8c963a1 commit 0b453c6

File tree

4 files changed

+56
-0
lines changed

4 files changed

+56
-0
lines changed

jupyter_server/auth/decorator.py

+27
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,30 @@ async def inner(self, *args, **kwargs):
8585
return cast(FuncT, wrapper(method))
8686

8787
return cast(FuncT, wrapper)
88+
89+
90+
def allow_unauthenticated(action: Optional[FuncT] = None) -> FuncT:
91+
"""A decorator for tornado.web.RequestHandler methods
92+
that allows any user to make the following request.
93+
94+
Selectively disables the 'authentication' layer of REST API which
95+
is active when `ServerApp.allow_unauthenticated_access = False`.
96+
97+
To be used exclusively on endpoints which may be considered public,
98+
for example the logic page handler.
99+
100+
.. versionadded:: 2.13
101+
102+
Parameters
103+
----------
104+
method : str
105+
the endpoint method to remove authentication from.
106+
"""
107+
108+
@wraps(method)
109+
async def wrapper(self, *args, **kwargs):
110+
return method(self, *args, **kwargs)
111+
112+
wrapper.__allow_unauthenticated = True
113+
114+
return cast(FuncT, wrapper)

jupyter_server/auth/login.py

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from tornado.escape import url_escape
1010

1111
from ..base.handlers import JupyterHandler
12+
from .decorator import allow_unauthenticated
1213
from .security import passwd_check, set_password
1314

1415

@@ -73,6 +74,7 @@ def _redirect_safe(self, url, default=None):
7374
url = default
7475
self.redirect(url)
7576

77+
@allow_unauthenticated
7678
def get(self):
7779
"""Get the login form."""
7880
if self.current_user:
@@ -81,6 +83,7 @@ def get(self):
8183
else:
8284
self._render()
8385

86+
@allow_unauthenticated
8487
def post(self):
8588
"""Post a login."""
8689
user = self.current_user = self.identity_provider.process_login_form(self)
@@ -110,6 +113,7 @@ def passwd_check(self, a, b):
110113
"""Check a passwd."""
111114
return passwd_check(a, b)
112115

116+
@allow_unauthenticated
113117
def post(self):
114118
"""Post a login form."""
115119
typed_password = self.get_argument("password", default="")

jupyter_server/base/handlers.py

+10
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,16 @@ async def prepare(self) -> Awaitable[None] | None: # type:ignore[override]
630630
self.set_cors_headers()
631631
if self.request.method not in {"GET", "HEAD", "OPTIONS"}:
632632
self.check_xsrf_cookie()
633+
634+
if not self.settings.get("allow_unauthenticated_access", False):
635+
if not self.request.method:
636+
raise HTTPError(403)
637+
method = getattr(self, self.request.method.lower())
638+
if not getattr(method, "__allow_unauthenticated", False):
639+
# reuse `web.authenticated` logic, which redirects to the login
640+
# page on GET and HEAD and otherwise raises 403
641+
return web.authenticated(lambda _method: None)(self)
642+
633643
return super().prepare()
634644

635645
# ---------------------------------------------------------------

jupyter_server/serverapp.py

+15
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,21 @@ def _deprecated_password_config(self, change: t.Any) -> None:
12141214
""",
12151215
)
12161216

1217+
allow_unauthenticated_access = Bool(
1218+
True,
1219+
config=True,
1220+
help="""Allow requests unauthenticated access to endpoints without authentication rules.
1221+
1222+
When set to `True` (default in jupyter-server 2.0, subject to change
1223+
in the future), any request to an endpoint without an authentication rule
1224+
(either `@tornado.web.authenticated`, or `@allow_unauthenticated`)
1225+
will be permitted, regardless of whether user has logged in or not.
1226+
1227+
When set to `False`, logging in will be required for access to each endpoint,
1228+
excluding the endpoints marked with `@allow_unauthenticated` decorator.
1229+
""",
1230+
)
1231+
12171232
allow_remote_access = Bool(
12181233
config=True,
12191234
help="""Allow requests where the Host header doesn't point to a local server

0 commit comments

Comments
 (0)