Compare commits

...

31 Commits

Author SHA1 Message Date
Min RK
a6a2056cca 2.0.0b2 2021-09-29 09:41:57 +02:00
Erik Sundell
fb1e81212f Merge pull request #3628 from minrk/beta-2
add latest changes to 2.0 changelog
2021-09-28 18:32:14 +02:00
Min RK
17f811d0b4 add latest changes to 2.0 changelog
- nullauthenticator
- lab by default
2021-09-28 15:28:59 +02:00
Min RK
34398d94de Merge pull request #3627 from jupyterhub/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-09-28 15:23:00 +02:00
Min RK
6bf94fde48 extend deadline for docker build test
it's building 4 images, 10 minutes isn't always enough, bump to 20
2021-09-28 14:46:33 +02:00
Min RK
ee18fed04b Merge pull request #3619 from manics/nullauthenticator
Add NullAuthenticator to jupyterhub
2021-09-28 11:36:41 +02:00
Simon Li
28f56ba510 Simplify NullAuthenticator, add test 2021-09-27 23:05:53 +01:00
pre-commit-ci[bot]
c8d3dbb7b1 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2021-09-27 19:45:58 +00:00
pre-commit-ci[bot]
a76a093638 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v2.26.0 → v2.28.0](https://github.com/asottile/pyupgrade/compare/v2.26.0...v2.28.0)
2021-09-27 19:43:18 +00:00
Erik Sundell
27908a8e17 Merge pull request #3616 from minrk/delete-scopes
add delete scopes for users, groups, servers
2021-09-27 14:01:51 +02:00
Erik Sundell
8a30f015c9 Merge pull request #3626 from minrk/token-log
server-api example typo: trim space in token file
2021-09-27 12:51:07 +02:00
Min RK
8cac83fc96 add delete scopes for users, groups, servers
e.g. cull-idle services do not need permission to start servers in order to be able to stop them
2021-09-27 12:43:56 +02:00
Min RK
9ade4bb9b2 server-api example: trim space in token file
avoids invalid newlines in the auth header
2021-09-27 12:42:23 +02:00
Min RK
874c91a086 Merge pull request #3615 from minrk/lab-by-default
2.0: jupyterlab by default
2021-09-27 12:39:31 +02:00
Min RK
a906677440 Merge pull request #3625 from albertmichaelj/main
Added base_url to path for jupyterhub-session-id cookie
2021-09-27 12:27:15 +02:00
pre-commit-ci[bot]
3f93942a24 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2021-09-26 19:55:05 +00:00
Michael Albert
aeb3130b25 Added base_url to path for jupyterhub_session_id cookie 2021-09-26 15:33:08 -04:00
Simon Li
8a6b364ca5 nullauthenticator: missing import 2021-09-23 20:40:00 +01:00
Simon Li
2ade7328d1 nullauthenticator: relative imports, entrypoint, doc 2021-09-23 20:39:54 +01:00
Min RK
2bb9f4f444 implement null authenticator 2021-09-23 19:14:07 +01:00
Simon Li
b029d983f9 Merge pull request #3607 from minrk/recommend-nodesource
update quickstart requirements
2021-09-23 17:31:18 +01:00
Min RK
4082006039 2.0: jupyterlab by default
swaps from default nbclassic and opt-in to lab, to now default to lab and opt-in to nbclassic

defaults to jupyterlab *if* lab 3.1 is available,
so should still work without configuration if lab is unavailable (or too old)
2021-09-23 14:52:14 +02:00
Min RK
69aa0eaa7a update quickstart requirements
- remove mention of outdated nodejs-legacy
- mention nodesource for more recent node
- mention jupyterlab
- initial localhost request will be on http, not https
2021-09-23 13:59:21 +02:00
Min RK
3674ada640 Merge pull request #3614 from jupyterhub/dependabot/npm_and_yarn/jsx/nth-check-2.0.1
Bump nth-check from 2.0.0 to 2.0.1 in /jsx
2021-09-23 13:56:00 +02:00
dependabot[bot]
48accb0a64 Bump nth-check from 2.0.0 to 2.0.1 in /jsx
Bumps [nth-check](https://github.com/fb55/nth-check) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/fb55/nth-check/releases)
- [Commits](https://github.com/fb55/nth-check/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: nth-check
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-22 09:01:52 +00:00
Simon Li
70ac143cfe Merge pull request #3613 from jupyterhub/dependabot/npm_and_yarn/jsx/tmpl-1.0.5
Bump tmpl from 1.0.4 to 1.0.5 in /jsx
2021-09-22 10:01:15 +01:00
dependabot[bot]
b1b2d531f8 Bump tmpl from 1.0.4 to 1.0.5 in /jsx
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

---
updated-dependencies:
- dependency-name: tmpl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-22 07:21:19 +00:00
Simon Li
e200783c59 Merge pull request #3610 from mriedem/patch-1
Fix 1.3 level
2021-09-20 21:28:54 +01:00
Erik Sundell
a7e57196c6 Merge pull request #3611 from jupyterhub/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-09-20 22:16:29 +02:00
pre-commit-ci[bot]
b5f05e6cd2 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 21.8b0 → 21.9b0](https://github.com/psf/black/compare/21.8b0...21.9b0)
- [github.com/pre-commit/mirrors-prettier: v2.4.0 → v2.4.1](https://github.com/pre-commit/mirrors-prettier/compare/v2.4.0...v2.4.1)
2021-09-20 19:57:49 +00:00
Matt Riedemann
5fe5b35f21 Fix 1.3 level
The 1.3 section was a sub-section of 1.4
which makes it harder to find 1.3 release notes
in the changelog from the navigation.
2021-09-20 13:40:45 -05:00
22 changed files with 262 additions and 59 deletions

View File

@@ -38,9 +38,9 @@ jobs:
# Tests everything when JupyterHub works against a dedicated mysql or
# postgresql server.
#
# jupyter_server:
# nbclassic:
# Tests everything when the user instances are started with
# jupyter_server instead of notebook.
# notebook instead of jupyter_server.
#
# ssl:
# Tests everything using internal SSL connections instead of
@@ -48,7 +48,7 @@ jobs:
#
# main_dependencies:
# Tests everything when the we use the latest available dependencies
# from: ipytraitlets.
# from: traitlets.
#
# NOTE: Since only the value of these parameters are presented in the
# GitHub UI when the workflow run, we avoid using true/false as
@@ -56,6 +56,7 @@ jobs:
include:
- python: "3.6"
oldest_dependencies: oldest_dependencies
nbclassic: nbclassic
- python: "3.6"
subdomain: subdomain
- python: "3.7"
@@ -65,7 +66,7 @@ jobs:
- python: "3.8"
db: postgres
- python: "3.8"
jupyter_server: jupyter_server
nbclassic: nbclassic
- python: "3.9"
main_dependencies: main_dependencies
@@ -130,9 +131,9 @@ jobs:
if [ "${{ matrix.main_dependencies }}" != "" ]; then
pip install git+https://github.com/ipython/traitlets#egg=traitlets --force
fi
if [ "${{ matrix.jupyter_server }}" != "" ]; then
pip uninstall notebook --yes
pip install jupyter_server
if [ "${{ matrix.nbclassic }}" != "" ]; then
pip uninstall jupyter_server --yes
pip install notebook
fi
if [ "${{ matrix.db }}" == "mysql" ]; then
pip install mysql-connector-python
@@ -194,7 +195,7 @@ jobs:
docker-build:
runs-on: ubuntu-20.04
timeout-minutes: 10
timeout-minutes: 20
steps:
- uses: actions/checkout@v2

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/asottile/pyupgrade
rev: v2.26.0
rev: v2.28.0
hooks:
- id: pyupgrade
args:
@@ -10,11 +10,11 @@ repos:
hooks:
- id: reorder-python-imports
- repo: https://github.com/psf/black
rev: 21.8b0
rev: 21.9b0
hooks:
- id: black
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.4.0
rev: v2.4.1
hooks:
- id: prettier
- repo: https://github.com/PyCQA/flake8

View File

@@ -7,8 +7,8 @@ codecov
coverage
cryptography
html5lib # needed for beautifulsoup
jupyterlab >=3
mock
notebook
pre-commit
pytest>=3.3
pytest-asyncio

File diff suppressed because one or more lines are too long

View File

@@ -5,8 +5,8 @@
Before installing JupyterHub, you will need:
- a Linux/Unix based system
- [Python](https://www.python.org/downloads/) 3.5 or greater. An understanding
of using [`pip`](https://pip.pypa.io/en/stable/) or
- [Python](https://www.python.org/downloads/) 3.6 or greater. An understanding
of using [`pip`](https://pip.pypa.io) or
[`conda`](https://conda.io/docs/get-started.html) for
installing Python packages is helpful.
- [nodejs/npm](https://www.npmjs.com/). [Install nodejs/npm](https://docs.npmjs.com/getting-started/installing-node),
@@ -20,11 +20,11 @@ Before installing JupyterHub, you will need:
For example, install it on Linux (Debian/Ubuntu) using:
```
sudo apt-get install npm nodejs-legacy
sudo apt-get install nodejs npm
```
The `nodejs-legacy` package installs the `node` executable and is currently
required for npm to work on Debian/Ubuntu.
[nodesource][] is a great resource to get more recent versions of the nodejs runtime,
if your system package manager only has an old version of Node.js (e.g. 10 or older).
- A [pluggable authentication module (PAM)](https://en.wikipedia.org/wiki/Pluggable_authentication_module)
to use the [default Authenticator](./getting-started/authenticators-users-basics.md).
@@ -33,11 +33,17 @@ Before installing JupyterHub, you will need:
- TLS certificate and key for HTTPS communication
- Domain name
[nodesource]: https://github.com/nodesource/distributions#table-of-contents
Before running the single-user notebook servers (which may be on the same
system as the Hub or not), you will need:
- [Jupyter Notebook](https://jupyter.readthedocs.io/en/latest/install.html)
version 4 or greater
- [JupyterLab][] version 3 or greater,
or [Jupyter Notebook][]
4 or greater.
[jupyterlab]: https://jupyterlab.readthedocs.io
[jupyter notebook]: https://jupyter.readthedocs.io/en/latest/install.html
## Installation
@@ -48,14 +54,14 @@ JupyterHub can be installed with `pip` (and the proxy with `npm`) or `conda`:
```bash
python3 -m pip install jupyterhub
npm install -g configurable-http-proxy
python3 -m pip install notebook # needed if running the notebook servers locally
python3 -m pip install jupyterlab notebook # needed if running the notebook servers in the same environment
```
**conda** (one command installs jupyterhub and proxy):
```bash
conda install -c conda-forge jupyterhub # installs jupyterhub and proxy
conda install notebook # needed if running the notebook servers locally
conda install jupyterlab notebook # needed if running the notebook servers in the same environment
```
Test your installation. If installed, these commands should return the packages'
@@ -74,7 +80,7 @@ To start the Hub server, run the command:
jupyterhub
```
Visit `https://localhost:8000` in your browser, and sign in with your unix
Visit `http://localhost:8000` in your browser, and sign in with your unix
credentials.
To **allow multiple users to sign in** to the Hub server, you must start

View File

@@ -76,13 +76,26 @@ c.InteractiveShellApp.extensions.append("cython")
### Example: Enable a Jupyter notebook configuration setting for all users
:::{note}
These examples configure the Jupyter ServerApp,
which is used by JupyterLab, the default in JupyterHub 2.0.
If you are using the classing Jupyter Notebook server,
the same things should work,
with the following substitutions:
- Where you see `jupyter_server_config`, use `jupyter_notebook_config`
- Where you see `NotebookApp`, use `ServerApp`
:::
To enable Jupyter notebook's internal idle-shutdown behavior (requires
notebook ≥ 5.4), set the following in the `/etc/jupyter/jupyter_notebook_config.py`
notebook ≥ 5.4), set the following in the `/etc/jupyter/jupyter_server_config.py`
file:
```python
# shutdown the server after no activity for an hour
c.NotebookApp.shutdown_no_activity_timeout = 60 * 60
c.ServerApp.shutdown_no_activity_timeout = 60 * 60
# shutdown kernels after no activity for 20 minutes
c.MappingKernelManager.cull_idle_timeout = 20 * 60
# check for idle kernels every two minutes
@@ -112,8 +125,8 @@ Assuming I have a Python 2 and Python 3 environment that I want to make
sure are available, I can install their specs system-wide (in /usr/local) with:
```bash
/path/to/python3 -m IPython kernel install --prefix=/usr/local
/path/to/python2 -m IPython kernel install --prefix=/usr/local
/path/to/python3 -m ipykernel install --prefix=/usr/local
/path/to/python2 -m ipykernel install --prefix=/usr/local
```
## Multi-user hosts vs. Containers
@@ -176,12 +189,40 @@ The number of named servers per user can be limited by setting
c.JupyterHub.named_server_limit_per_user = 5
```
## Switching to Jupyter Server
(classic-notebook-ui)=
[Jupyter Server](https://jupyter-server.readthedocs.io/en/latest/) is a new Tornado Server backend for Jupyter web applications (e.g. JupyterLab 3.0 uses this package as its default backend).
## Switching back to classic notebook
By default, the single-user notebook server uses the (old) `NotebookApp` from the [notebook](https://github.com/jupyter/notebook) package. You can switch to using Jupyter Server's `ServerApp` backend (this will likely become the default in future releases) by setting the `JUPYTERHUB_SINGLEUSER_APP` environment variable to:
By default the single-user server launches JupyterLab,
which is based on [Jupyter Server][].
This is the default server when running JupyterHub ≥ 2.0.
You can switch to using the legacy Jupyter Notebook server by setting the `JUPYTERHUB_SINGLEUSER_APP` environment variable
(in the single-user environment) to:
```bash
export JUPYTERHUB_SINGLEUSER_APP='notebook.notebookapp.NotebookApp'
```
[jupyter server]: https://jupyter-server.readthedocs.io
[jupyter notebook]: https://jupyter-notebook.readthedocs.io
:::{versionchanged} 2.0
JupyterLab is now the default singleuser UI, if available,
which is based on the [Jupyter Server][],
no longer the legacy [Jupyter Notebook][] server.
JupyterHub prior to 2.0 launched the legacy notebook server (`jupyter notebook`),
and Jupyter server could be selected by specifying
```python
# jupyterhub_config.py
c.Spawner.cmd = ["jupyter-labhub"]
```
or for an otherwise customized Jupyter Server app,
set the environment variable:
```bash
export JUPYTERHUB_SINGLEUSER_APP='jupyter_server.serverapp.ServerApp'
```
:::

View File

@@ -29,7 +29,7 @@ def get_token():
token_file = here.joinpath("service-token")
log.info(f"Loading token from {token_file}")
with token_file.open("r") as f:
token = f.read()
token = f.read().strip()
return token

View File

@@ -5457,9 +5457,9 @@ npm-run-path@^4.0.0:
path-key "^3.0.0"
nth-check@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==
version "2.0.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2"
integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==
dependencies:
boolbase "^1.0.0"
@@ -7278,9 +7278,9 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.3:
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
tmpl@1.0.x:
version "1.0.4"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=
version "1.0.5"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
to-fast-properties@^2.0.0:
version "2.0.0"

View File

@@ -6,7 +6,7 @@ version_info = (
2,
0,
0,
"b1", # release (b1, rc1, or "" for final or dev)
"b2", # release (b1, rc1, or "" for final or dev)
# "dev", # dev or nothing for beta/rc/stable releases
)

View File

@@ -129,7 +129,7 @@ class GroupAPIHandler(_GroupAPIHandler):
self.write(json.dumps(self.group_model(group)))
self.set_status(201)
@needs_scope('admin:groups')
@needs_scope('delete:groups')
def delete(self, group_name):
"""Delete a group by name"""
group = self.find_group(group_name)

View File

@@ -266,7 +266,7 @@ class UserAPIHandler(APIHandler):
self.write(json.dumps(self.user_model(user)))
self.set_status(201)
@needs_scope('admin:users')
@needs_scope('delete:users')
async def delete(self, user_name):
user = self.find_user(user_name)
if user is None:
@@ -525,7 +525,7 @@ class UserServerAPIHandler(APIHandler):
self.set_header('Content-Type', 'text/plain')
self.set_status(status)
@needs_scope('servers')
@needs_scope('delete:servers')
async def delete(self, user_name, server_name=''):
user = self.find_user(user_name)
options = self.get_json_body()

View File

@@ -1173,3 +1173,22 @@ class DummyAuthenticator(Authenticator):
return data['username']
return None
return data['username']
class NullAuthenticator(Authenticator):
"""Null Authenticator for JupyterHub
For cases where authentication should be disabled,
e.g. only allowing access via API tokens.
.. versionadded:: 2.0
"""
# auto_login skips 'Login with...' page on Hub 0.8
auto_login = True
# for Hub 0.7, show 'login with...'
login_service = 'null'
def get_handlers(self, app):
return []

View File

@@ -490,7 +490,7 @@ class BaseHandler(RequestHandler):
session_id = self.get_session_cookie()
if session_id:
# clear session id
self.clear_cookie(SESSION_COOKIE_NAME, **kwargs)
self.clear_cookie(SESSION_COOKIE_NAME, path=self.base_url, **kwargs)
if user:
# user is logged in, clear any tokens associated with the current session
@@ -569,7 +569,9 @@ class BaseHandler(RequestHandler):
so other services on this domain can read it.
"""
session_id = uuid.uuid4().hex
self._set_cookie(SESSION_COOKIE_NAME, session_id, encrypted=False)
self._set_cookie(
SESSION_COOKIE_NAME, session_id, encrypted=False, path=self.base_url
)
return session_id
def set_service_cookie(self, user):

View File

@@ -89,6 +89,7 @@ def expand_self_scope(name):
'users:activity',
'read:users:activity',
'servers',
'delete:servers',
'read:servers',
'tokens',
'read:tokens',

View File

@@ -36,13 +36,16 @@ scope_definitions = {
},
'admin:users': {
'description': 'Read, write, create and delete users and their authentication state, not including their servers or tokens.',
'subscopes': ['admin:auth_state', 'users', 'read:roles:users'],
'subscopes': ['admin:auth_state', 'users', 'read:roles:users', 'delete:users'],
},
'admin:auth_state': {'description': 'Read a users authentication state.'},
'users': {
'description': 'Read and write permissions to user models (excluding servers, tokens and authentication state).',
'subscopes': ['read:users', 'list:users', 'users:activity'],
},
'delete:users': {
'description': "Delete users.",
},
'list:users': {
'description': 'List users, including at least their names.',
'subscopes': ['read:users:name'],
@@ -76,12 +79,13 @@ scope_definitions = {
'admin:server_state': {'description': 'Read and write users server state.'},
'servers': {
'description': 'Start and stop user servers.',
'subscopes': ['read:servers'],
'subscopes': ['read:servers', 'delete:servers'],
},
'read:servers': {
'description': 'Read users names and their server models (excluding the server state).',
'subscopes': ['read:users:name'],
},
'delete:servers': {'description': "Stop and delete users' servers."},
'tokens': {
'description': 'Read, write, create and delete user tokens.',
'subscopes': ['read:tokens'],
@@ -89,7 +93,7 @@ scope_definitions = {
'read:tokens': {'description': 'Read user tokens.'},
'admin:groups': {
'description': 'Read and write group information, create and delete groups.',
'subscopes': ['groups', 'read:roles:groups'],
'subscopes': ['groups', 'read:roles:groups', 'delete:groups'],
},
'groups': {
'description': 'Read and write group information, including adding/removing users to/from groups.',
@@ -104,6 +108,9 @@ scope_definitions = {
'subscopes': ['read:groups:name'],
},
'read:groups:name': {'description': 'Read group names.'},
'delete:groups': {
'description': "Delete groups.",
},
'list:services': {
'description': 'List services, including at least their names.',
'subscopes': ['read:services:name'],

View File

@@ -1,7 +1,12 @@
"""Make a single-user app based on the environment:
- $JUPYTERHUB_SINGLEUSER_APP, the base Application class, to be wrapped in JupyterHub authentication.
default: notebook.notebookapp.NotebookApp
default: jupyter_server.serverapp.ServerApp
.. versionchanged:: 2.0
Default app changed to launch `jupyter labhub`.
Use JUPYTERHUB_SINGLEUSER_APP=notebook.notebookapp.NotebookApp for the legacy 'classic' notebook server.
"""
import os
@@ -9,12 +14,55 @@ from traitlets import import_item
from .mixins import make_singleuser_app
JUPYTERHUB_SINGLEUSER_APP = (
os.environ.get("JUPYTERHUB_SINGLEUSER_APP") or "notebook.notebookapp.NotebookApp"
)
JUPYTERHUB_SINGLEUSER_APP = os.environ.get("JUPYTERHUB_SINGLEUSER_APP")
if JUPYTERHUB_SINGLEUSER_APP:
App = import_item(JUPYTERHUB_SINGLEUSER_APP)
else:
App = None
_import_error = None
for JUPYTERHUB_SINGLEUSER_APP in (
"jupyter_server.serverapp.ServerApp",
"notebook.notebookapp.NotebookApp",
):
try:
App = import_item(JUPYTERHUB_SINGLEUSER_APP)
except ImportError as e:
continue
if _import_error is None:
_import_error = e
else:
break
if App is None:
raise _import_error
App = import_item(JUPYTERHUB_SINGLEUSER_APP)
SingleUserNotebookApp = make_singleuser_app(App)
main = SingleUserNotebookApp.launch_instance
def main():
"""Launch a jupyterhub single-user server"""
if not os.environ.get("JUPYTERHUB_SINGLEUSER_APP"):
# app not specified, launch jupyter-labhub by default,
# if jupyterlab is recent enough (3.1).
# This is a minimally extended ServerApp that does:
# 1. ensure lab extension is enabled, and
# 2. set default URL to `/lab`
import re
_version_pat = re.compile(r"(\d+)\.(\d+)")
try:
import jupyterlab
from jupyterlab.labhubapp import SingleUserLabApp
m = _version_pat.match(jupyterlab.__version__)
except Exception:
m = None
if m is not None:
version_tuple = tuple(int(v) for v in m.groups())
if version_tuple >= (3, 1):
return SingleUserLabApp.launch_instance()
return SingleUserNotebookApp.launch_instance()

View File

@@ -3,6 +3,7 @@
# Distributed under the terms of the Modified BSD License.
import logging
from unittest import mock
from urllib.parse import urlparse
import pytest
from requests import HTTPError
@@ -12,6 +13,8 @@ from .mocking import MockPAMAuthenticator
from .mocking import MockStructGroup
from .mocking import MockStructPasswd
from .utils import add_user
from .utils import async_requests
from .utils import public_url
from jupyterhub import auth
from jupyterhub import crypto
from jupyterhub import orm
@@ -515,3 +518,12 @@ def test_deprecated_methods_subclass():
assert authenticator.check_whitelist("subclass-allowed")
assert not authenticator.check_allowed("otheruser")
assert not authenticator.check_whitelist("otheruser")
async def test_nullauthenticator(app):
with mock.patch.dict(
app.tornado_settings, {"authenticator": auth.NullAuthenticator(parent=app)}
):
r = await async_requests.get(public_url(app))
assert urlparse(r.url).path.endswith("/hub/login")
assert r.status_code == 403

View File

@@ -182,6 +182,7 @@ def test_orm_roles_delete_cascade(db):
'admin:users',
'admin:auth_state',
'users',
'delete:users',
'list:users',
'read:users',
'users:activity',
@@ -218,6 +219,7 @@ def test_orm_roles_delete_cascade(db):
{
'admin:groups',
'groups',
'delete:groups',
'list:groups',
'read:groups',
'read:roles:groups',
@@ -229,6 +231,7 @@ def test_orm_roles_delete_cascade(db):
{
'admin:groups',
'groups',
'delete:groups',
'list:groups',
'read:groups',
'read:roles:groups',

View File

@@ -1,6 +1,10 @@
"""Tests for jupyterhub.singleuser"""
import os
import sys
from contextlib import contextmanager
from subprocess import CalledProcessError
from subprocess import check_output
from unittest import mock
from urllib.parse import urlparse
import pytest
@@ -14,6 +18,12 @@ from .utils import async_requests
from .utils import AsyncSession
@contextmanager
def nullcontext():
"""Python 3.7+ contextlib.nullcontext, backport for 3.6"""
yield
@pytest.mark.parametrize(
"access_scopes, server_name, expect_success",
[
@@ -171,3 +181,47 @@ def test_version():
[sys.executable, '-m', 'jupyterhub.singleuser', '--version']
).decode('utf8', 'replace')
assert jupyterhub.__version__ in out
@pytest.mark.parametrize(
"JUPYTERHUB_SINGLEUSER_APP",
[
"",
"notebook.notebookapp.NotebookApp",
"jupyter_server.serverapp.ServerApp",
],
)
def test_singleuser_app_class(JUPYTERHUB_SINGLEUSER_APP):
try:
import jupyter_server # noqa
except ImportError:
have_server = False
expect_error = "jupyter_server" in JUPYTERHUB_SINGLEUSER_APP
else:
have_server = True
expect_error = False
if expect_error:
ctx = pytest.raises(CalledProcessError)
else:
ctx = nullcontext()
with mock.patch.dict(
os.environ,
{
"JUPYTERHUB_SINGLEUSER_APP": JUPYTERHUB_SINGLEUSER_APP,
},
):
with ctx:
out = check_output(
[sys.executable, '-m', 'jupyterhub.singleuser', '--help-all']
).decode('utf8', 'replace')
if expect_error:
return
# use help-all output to check inheritance
if 'NotebookApp' in JUPYTERHUB_SINGLEUSER_APP or not have_server:
assert '--NotebookApp.' in out
assert '--ServerApp.' not in out
else:
assert '--ServerApp.' in out
assert '--NotebookApp.' not in out

View File

@@ -84,7 +84,7 @@ class UserDict(dict):
if user.name == key:
key = user.id
break
return dict.__contains__(self, key)
return super().__contains__(key)
def __getitem__(self, key):
"""UserDict allows retrieval of user by any of:
@@ -108,7 +108,7 @@ class UserDict(dict):
if orm_user.id not in self:
user = self[orm_user.id] = User(orm_user, self.settings)
return user
user = dict.__getitem__(self, orm_user.id)
user = super().__getitem__(orm_user.id)
user.db = self.db
return user
elif isinstance(key, int):
@@ -119,7 +119,7 @@ class UserDict(dict):
raise KeyError("No such user: %s" % id)
user = self.add(orm_user)
else:
user = dict.__getitem__(self, id)
user = super().__getitem__(id)
return user
else:
raise KeyError(repr(key))
@@ -145,7 +145,7 @@ class UserDict(dict):
self.db.expunge(orm_spawner)
if user.orm_user in self.db:
self.db.expunge(user.orm_user)
dict.__delitem__(self, user.id)
super().__delitem__(user.id)
def delete(self, key):
"""Delete a user from the cache and the database"""

View File

@@ -100,6 +100,7 @@ setup_args = dict(
'default = jupyterhub.auth:PAMAuthenticator',
'pam = jupyterhub.auth:PAMAuthenticator',
'dummy = jupyterhub.auth:DummyAuthenticator',
'null = jupyterhub.auth:NullAuthenticator',
],
'jupyterhub.proxies': [
'default = jupyterhub.proxy:ConfigurableHTTPProxy',
@@ -301,7 +302,7 @@ class develop_js_css(develop):
if not self.uninstall:
self.distribution.run_command('js')
self.distribution.run_command('css')
develop.run(self)
super().run()
setup_args['cmdclass']['develop'] = develop_js_css

View File

@@ -7,6 +7,6 @@ MAINTAINER Project Jupyter <jupyter@googlegroups.com>
ADD install_jupyterhub /tmp/install_jupyterhub
ARG JUPYTERHUB_VERSION=main
# install pinned jupyterhub and ensure notebook is installed
# install pinned jupyterhub and ensure jupyterlab is installed
RUN python3 /tmp/install_jupyterhub && \
python3 -m pip install notebook
python3 -m pip install jupyterlab