From 598a88f803e52575deaeea909b871c89aeb3f6f6 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 28 Oct 2021 19:14:52 +0200 Subject: [PATCH 1/3] ci: test against py 3.10 and jupyterhub 2 --- .github/workflows/publish.yaml | 2 +- .github/workflows/test.yaml | 19 +++++++++++++------ .pre-commit-config.yaml | 0 3 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 45839ff..d1e341b 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -1,5 +1,5 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions # # Publish to PyPI on push of version like tags # diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 2e27497..546b524 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,5 +1,5 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions # name: Test @@ -35,20 +35,27 @@ jobs: fail-fast: false matrix: include: - - python-version: 3.6 - - python-version: 3.7 - - python-version: 3.8 - - python-version: 3.9 + - python-version: "3.6" + jupyterhub-version: "1.3.*" + - python-version: "3.7" + jupyterhub-version: "1.4.*" + - python-version: "3.8" + jupyterhub-version: "1.*" + - python-version: "3.9" + jupyterhub-version: "2.*" + - python-version: "3.10" + jupyterhub-version: "2.*" steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: - python-version: "${{ matrix.python-version}}" + python-version: "${{ matrix.python-version }}" - name: Install Python dependencies run: | pip install --no-cache-dir -r dev-requirements.txt + pip install --pre "jupyterhub==${{ matrix.jupyterhub-version }}" pip install . - name: Run tests diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..e69de29 From 30ac4a68870cc8b0902f46c8e022fcb14b56ef4c Mon Sep 17 00:00:00 2001 From: csears-hg Date: Wed, 27 Oct 2021 09:56:08 -0400 Subject: [PATCH 2/3] Replaced deprecated admin_only with new need_scope JupyterHub 2.0 deprecated the admin_only decorator in https://github.com/jupyterhub/jupyterhub/pull/3659. Per the deprecation warning, I'm replacing admin_only with the new needs_scope decorator, using the scope 'admin:users'. In testing, this produced 403 errors for non-admin users as expected. --- nativeauthenticator/handlers.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nativeauthenticator/handlers.py b/nativeauthenticator/handlers.py index fd347d6..ab92e0c 100644 --- a/nativeauthenticator/handlers.py +++ b/nativeauthenticator/handlers.py @@ -4,7 +4,7 @@ from jinja2 import ChoiceLoader, FileSystemLoader from jupyterhub.handlers import BaseHandler from jupyterhub.handlers.login import LoginHandler -from jupyterhub.utils import admin_only +from jupyterhub.scopes import needs_scope from tornado import web from tornado.escape import url_escape @@ -145,7 +145,7 @@ async def post(self): class AuthorizationHandler(LocalBase): """Render the sign in page.""" - @admin_only + @needs_scope('admin:users') async def get(self): html = await self.render_template( 'autorization-area.html', @@ -156,7 +156,7 @@ async def get(self): class ChangeAuthorizationHandler(LocalBase): - @admin_only + @needs_scope('admin:users') async def get(self, slug): UserInfo.change_authorization(self.db, slug) self.redirect(self.hub.base_url + 'authorize#' + slug) @@ -256,7 +256,7 @@ async def post(self): class ChangePasswordAdminHandler(LocalBase): """Render the reset password page.""" - @admin_only + @needs_scope('admin:users') async def get(self, user_name): if not self.authenticator.user_exists(user_name): raise web.HTTPError(404) @@ -266,7 +266,7 @@ async def get(self, user_name): ) self.finish(html) - @admin_only + @needs_scope('admin:users') async def post(self, user_name): new_password = self.get_body_argument('password', strip=False) self.authenticator.change_password(user_name, new_password) @@ -334,7 +334,7 @@ async def post(self): class DiscardHandler(LocalBase): """Discard a user from database""" - @admin_only + @needs_scope('admin:users') async def get(self, user_name): user = self.authenticator.get_user(user_name) if user is not None: From c3d07afb440c22959b4f60bf3cac46505885b173 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 28 Oct 2021 19:24:46 +0200 Subject: [PATCH 3/3] Support new JupyterHub RBAC while being backward compatible --- nativeauthenticator/handlers.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/nativeauthenticator/handlers.py b/nativeauthenticator/handlers.py index ab92e0c..ea5a5eb 100644 --- a/nativeauthenticator/handlers.py +++ b/nativeauthenticator/handlers.py @@ -4,7 +4,12 @@ from jinja2 import ChoiceLoader, FileSystemLoader from jupyterhub.handlers import BaseHandler from jupyterhub.handlers.login import LoginHandler -from jupyterhub.scopes import needs_scope +try: + from jupyterhub.scopes import needs_scope + admin_users_scope = needs_scope("admin:users") +except ImportError: + from jupyterhub.utils import admin_only + admin_users_scope = admin_only from tornado import web from tornado.escape import url_escape @@ -145,7 +150,7 @@ async def post(self): class AuthorizationHandler(LocalBase): """Render the sign in page.""" - @needs_scope('admin:users') + @admin_users_scope async def get(self): html = await self.render_template( 'autorization-area.html', @@ -156,7 +161,7 @@ async def get(self): class ChangeAuthorizationHandler(LocalBase): - @needs_scope('admin:users') + @admin_users_scope async def get(self, slug): UserInfo.change_authorization(self.db, slug) self.redirect(self.hub.base_url + 'authorize#' + slug) @@ -256,7 +261,7 @@ async def post(self): class ChangePasswordAdminHandler(LocalBase): """Render the reset password page.""" - @needs_scope('admin:users') + @admin_users_scope async def get(self, user_name): if not self.authenticator.user_exists(user_name): raise web.HTTPError(404) @@ -266,7 +271,7 @@ async def get(self, user_name): ) self.finish(html) - @needs_scope('admin:users') + @admin_users_scope async def post(self, user_name): new_password = self.get_body_argument('password', strip=False) self.authenticator.change_password(user_name, new_password) @@ -334,7 +339,7 @@ async def post(self): class DiscardHandler(LocalBase): """Discard a user from database""" - @needs_scope('admin:users') + @admin_users_scope async def get(self, user_name): user = self.authenticator.get_user(user_name) if user is not None: