diff --git a/requirements/dev.txt b/requirements/dev.txt index 3af7162..f4aceaa 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with python 3.10 +# This file is autogenerated by pip-compile with python 3.8 # To update, run: # # pip-compile --generate-hashes --output-file=requirements/dev.txt requirements/dev.in diff --git a/requirements/main.txt b/requirements/main.txt index f3c9314..adf40b1 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with python 3.10 +# This file is autogenerated by pip-compile with python 3.8 # To update, run: # # pip-compile --generate-hashes --output-file=requirements/main.txt requirements/main.in @@ -137,9 +137,9 @@ cchardet==2.1.7 \ --hash=sha256:f86e0566cb61dc4397297696a4a1b30f6391b50bc52b4f073507a48466b6255a \ --hash=sha256:fdac1e4366d0579fff056d1280b8dc6348be964fda8ebb627c0269e097ab37fa # via -r requirements/main.in -certifi==2021.10.8 \ - --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ - --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 +certifi==2022.5.18 \ + --hash=sha256:6ae10321df3e464305a46e997da41ea56c1d311fb9ff1dd4e04d6f14653ec63a \ + --hash=sha256:8d15a5a7fde18536a249c49e07e8e462b8fc13de21b3c80e8a68315dfa227c99 # via # kubernetes-asyncio # requests @@ -368,11 +368,16 @@ idna==3.3 \ importlib-metadata==4.11.3 \ --hash=sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6 \ --hash=sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539 - # via -r requirements/main.in + # via + # -r requirements/main.in + # alembic importlib-resources==5.7.1 \ --hash=sha256:b6062987dfc51f0fcb809187cffbd60f35df7acb4589091f154214af6d0d49d3 \ --hash=sha256:e447dc01619b1e951286f3929be820029d48c75eb25d265c28b92a16548212b8 - # via -r requirements/main.in + # via + # -r requirements/main.in + # alembic + # jsonschema inflect==5.6.0 \ --hash=sha256:967d6db69932bac9f1977b8f2dd131a59147f4b56134cfc74a3f44e5adb65223 \ --hash=sha256:a0612e7bba1028bb7efa121bf8f012aeda9355252d01b257057fa2a8f5859cef @@ -402,9 +407,9 @@ jupyterhub-idle-culler==1.2.1 \ --hash=sha256:c84e45a51932a34bd95c08b3b6e8330fb0ee3391fa4d0f1ac0d4458a40492fd9 \ --hash=sha256:d80822982d2590cb876d01849cee028943fdbdcc991c0f02fce45af3f40dd415 # via -r requirements/main.in -jupyterhub-kubespawner==4.0.0 \ - --hash=sha256:09d6005aa722a99f0a7e3127c6029465e5603451d4c0077fcef32834dba2892a \ - --hash=sha256:d06a6f90711c6748fc48f681994f04f1240d23117c341b1f3ac9897fd9ef3da2 +jupyterhub-kubespawner==4.1.0 \ + --hash=sha256:77c669e00586529534aefe6ae8e6a39588b8ca3d58d21f1be4742b47062d78cf \ + --hash=sha256:7fbcdf5ca7bd36bbe64c5ac5e76bdf76faa609949652ec6dc5b2f527542b55a9 # via -r requirements/main.in kubernetes-asyncio==22.6.4 \ --hash=sha256:a552211c72c7452999d7bf3b09326a0557899d749b001743aa63f7fc3fcbcb2c \ @@ -805,9 +810,9 @@ tornado==6.1 \ # -r requirements/main.in # jupyterhub # jupyterhub-idle-culler -traitlets==5.2.0 \ - --hash=sha256:60474f39bf1d39a11e0233090b99af3acee93bbc2281777e61dd8c87da8a0014 \ - --hash=sha256:9dd4025123fbe018a2092b2ad6984792f53ea3362c698f37473258b1fa97b0bc +traitlets==5.2.1.post0 \ + --hash=sha256:70815ecb20ec619d1af28910ade523383be13754283aef90528eb3d47b77c5db \ + --hash=sha256:f44b708d33d98b0addb40c29d148a761f44af740603a8fd0e2f8b5b27cf0f087 # via # jupyter-telemetry # jupyterhub @@ -895,7 +900,9 @@ yarl==1.7.2 \ zipp==3.8.0 \ --hash=sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad \ --hash=sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099 - # via importlib-metadata + # via + # importlib-metadata + # importlib-resources # WARNING: The following packages were not pinned, but pip requires them to be # pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag. diff --git a/src/nublado2/auth.py b/src/nublado2/auth.py index 1b62ea6..0f45458 100644 --- a/src/nublado2/auth.py +++ b/src/nublado2/auth.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING from jupyterhub.auth import Authenticator -from jupyterhub.handlers import BaseHandler +from jupyterhub.handlers import BaseHandler, LogoutHandler from jupyterhub.utils import url_path_join from tornado import web @@ -140,8 +140,11 @@ async def authenticate( raise NotImplementedError() def get_handlers(self, app: JupyterHub) -> List[Route]: - """Register the header-only login handler.""" - return [("/gafaelfawr/login", GafaelfawrLoginHandler)] + """Register the header-only login and the logout handlers.""" + return [ + ("/gafaelfawr/login", GafaelfawrLoginHandler), + ("/logout", GafaelfawrLogoutHandler), + ] def login_url(self, base_url: str) -> str: """Override the login URL. @@ -178,6 +181,22 @@ async def refresh_user( return await _build_auth_info(handler.request.headers) +class GafaelfawrLogoutHandler(LogoutHandler): + """Logout handler for Gafaelfawr authentication. + + A logout should always stop all running servers, and then redirect to the + RSP logout page. + """ + + @property + def shutdown_on_logout(self) -> bool: + """Unconditionally true for Gafaelfawr logout""" + return True + + async def render_logout_page(self) -> None: + self.redirect("/logout", permanent=False) + + class GafaelfawrLoginHandler(BaseHandler): """Login handler for Gafaelfawr authentication. diff --git a/tests/auth_test.py b/tests/auth_test.py index d30a61c..8e83245 100644 --- a/tests/auth_test.py +++ b/tests/auth_test.py @@ -19,6 +19,7 @@ from nublado2.auth import ( GafaelfawrAuthenticator, GafaelfawrLoginHandler, + GafaelfawrLogoutHandler, _build_auth_info, ) @@ -36,7 +37,8 @@ async def config_mock() -> AsyncGenerator: def test_authenticator() -> None: authenticator = GafaelfawrAuthenticator() assert authenticator.get_handlers(MagicMock()) == [ - ("/gafaelfawr/login", GafaelfawrLoginHandler) + ("/gafaelfawr/login", GafaelfawrLoginHandler), + ("/logout", GafaelfawrLogoutHandler), ] assert authenticator.login_url("/hub") == "/hub/gafaelfawr/login"