Merge pull request #395 from GeorgianaElena/admin_pass_on_setup

Set admin password during install
This commit is contained in:
Yuvi Panda
2020-01-29 08:12:59 -08:00
committed by GitHub
8 changed files with 121 additions and 15 deletions

View File

@@ -72,6 +72,12 @@ jobs:
command: | command: |
.circleci/integration-test.py run-test basic-tests test_hub.py test_install.py test_extensions.py .circleci/integration-test.py run-test basic-tests test_hub.py test_install.py test_extensions.py
- run:
name: Run admin tests
command: |
.circleci/integration-test.py run-test --installer-args "--admin admin:admin" basic-tests test_admin_installer.py
- run: - run:
name: Run plugin tests name: Run plugin tests
command: | command: |

View File

@@ -43,6 +43,14 @@ The easiest & safest way to develop & test TLJH is with `Docker <https://www.doc
python3 /srv/src/bootstrap/bootstrap.py --admin admin python3 /srv/src/bootstrap/bootstrap.py --admin admin
Or, if you would like to setup the admin's password during install,
you can use this command (replace "admin" with the desired admin username
and "password" with the desired admin password):
.. code-block:: console
python3 /srv/src/bootstrap/bootstrap.py --admin admin:password
The primary hub environment will also be in your PATH already for convenience. The primary hub environment will also be in your PATH already for convenience.
#. You should be able to access the JupyterHub from your browser now at #. You should be able to access the JupyterHub from your browser now at

View File

@@ -19,7 +19,8 @@ so attackers can not easily gain control of the system.
.. important:: .. important::
You should make sure an admin user is present when you **install** TLJH You should make sure an admin user is present when you **install** TLJH
the very first time. The ``:ref:`--admin <topic/customizing-installer/admin>``` the very first time. It is recommended that you also set a password
for the admin at this step. The :ref:`--admin <topic/customizing-installer/admin>`
flag passed to the installer does this. If you had forgotten to do so, the flag passed to the installer does this. If you had forgotten to do so, the
easiest way to fix this is to run the installer again. easiest way to fix this is to run the installer again.

View File

@@ -20,11 +20,24 @@ This page documents the various options you can pass as commandline parameters t
Adding admin users Adding admin users
=================== ===================
``--admin <username>`` adds user ``<username>`` to JupyterHub as an admin user. ``--admin <username>:<password>`` adds user ``<username>`` to JupyterHub as an admin user
This can be repeated multiple times. and sets its password to be ``<password>``.
Although it is not recommended, it is possible to only set the admin username at this point
and set the admin password after the installation.
For example, to add ``admin-user1`` and ``admin-user2`` as admins when installing, you Also, the ``--admin`` flag can be repeated multiple times. For example, to add ``admin-user1``
would do: and ``admin-user2`` as admins when installing, depending if you would like to set their passwords
during install you would:
* set ``admin-user1`` with password ``password-user1`` and ``admin-user2`` with ``password-user2`` using:
.. code-block:: bash
curl https://raw.githubusercontent.com/jupyterhub/the-littlest-jupyterhub/master/bootstrap/bootstrap.py \
| sudo python3 - \
--admin admin-user1:password-user1 --admin admin-user2:password-user2
* set ``admin-user1`` and ``admin-user2`` to be admins, without any passwords at this stage, using:
.. code-block:: bash .. code-block:: bash
@@ -32,6 +45,14 @@ would do:
| sudo python3 - \ | sudo python3 - \
--admin admin-user1 --admin admin-user2 --admin admin-user1 --admin admin-user2
* set ``admin-user1`` with password ``password-user1`` and ``admin-user2`` with no password at this stage using:
.. code-block:: bash
curl https://raw.githubusercontent.com/jupyterhub/the-littlest-jupyterhub/master/bootstrap/bootstrap.py \
| sudo python3 - \
--admin admin-user1:password-user1 --admin admin-user2
Installing python packages in the user environment Installing python packages in the user environment
================================================== ==================================================

View File

@@ -0,0 +1,45 @@
from hubtraf.user import User
from hubtraf.auth.dummy import login_dummy
import pytest
from functools import partial
import asyncio
@pytest.mark.asyncio
async def test_admin_login():
"""
Test if the admin that was added during install can login with
the password provided.
"""
hub_url = 'http://localhost'
username = "admin"
password = "admin"
async with User(username, hub_url, partial(login_dummy, password=password)) as u:
await u.login()
await u.ensure_server()
@pytest.mark.asyncio
@pytest.mark.parametrize(
"username, password",
[
("admin", ""),
("admin", "wrong_passw"),
("user", "password"),
],
)
async def test_unsuccessful_login(username, password):
"""
Ensure nobody but the admin that was added during install can login
"""
hub_url = 'http://localhost'
try:
async with User(username, hub_url, partial(login_dummy, password="")) as u:
await u.login()
except Exception:
# This is what we except to happen
pass
else:
raise

View File

@@ -17,6 +17,7 @@ setup(
'passlib', 'passlib',
'backoff', 'backoff',
'requests', 'requests',
'bcrypt',
'jupyterhub-traefik-proxy==0.1.*' 'jupyterhub-traefik-proxy==0.1.*'
], ],
entry_points={ entry_points={

View File

@@ -2,6 +2,7 @@
Unit test functions in installer.py Unit test functions in installer.py
""" """
import os import os
import pytest
from tljh import installer from tljh import installer
from tljh.yaml import yaml from tljh.yaml import yaml
@@ -21,10 +22,17 @@ def test_ensure_config_yaml(tljh_dir):
# verify that old config doesn't exist # verify that old config doesn't exist
assert not os.path.exists(os.path.join(tljh_dir, 'config.yaml')) assert not os.path.exists(os.path.join(tljh_dir, 'config.yaml'))
def test_ensure_admins(tljh_dir):
@pytest.mark.parametrize(
"admins, expected_config",
[
([['a1'], ['a2'], ['a3']], ['a1', 'a2', 'a3']),
([['a1:p1'], ['a2']], ['a1', 'a2']),
],
)
def test_ensure_admins(tljh_dir, admins, expected_config):
# --admin option called multiple times on the installer # --admin option called multiple times on the installer
# creates a list of argument lists. # creates a list of argument lists.
admins = [['a1'], ['a2'], ['a3']]
installer.ensure_admins(admins) installer.ensure_admins(admins)
config_path = installer.CONFIG_FILE config_path = installer.CONFIG_FILE
@@ -32,4 +40,4 @@ def test_ensure_admins(tljh_dir):
config = yaml.load(f) config = yaml.load(f)
# verify the list was flattened # verify the list was flattened
assert config['users']['admin'] == ['a1', 'a2', 'a3'] assert config['users']['admin'] == expected_config

View File

@@ -1,6 +1,7 @@
"""Installation logic for TLJH""" """Installation logic for TLJH"""
import argparse import argparse
import dbm
import itertools import itertools
import logging import logging
import os import os
@@ -10,9 +11,11 @@ import sys
import time import time
import warnings import warnings
import bcrypt
import pluggy import pluggy
import requests import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning from requests.packages.urllib3.exceptions import InsecureRequestWarning
from getpass import getpass
from tljh import ( from tljh import (
apt, apt,
@@ -126,8 +129,6 @@ def ensure_jupyterhub_service(prefix):
Ensure JupyterHub Services are set up properly Ensure JupyterHub Services are set up properly
""" """
os.makedirs(STATE_DIR, mode=0o700, exist_ok=True)
remove_chp() remove_chp()
systemd.reload_daemon() systemd.reload_daemon()
@@ -271,11 +272,13 @@ def ensure_user_environment(user_requirements_txt_file):
conda.ensure_pip_requirements(USER_ENV_PREFIX, user_requirements_txt_file) conda.ensure_pip_requirements(USER_ENV_PREFIX, user_requirements_txt_file)
def ensure_admins(admins): def ensure_admins(admin_password_list):
""" """
Setup given list of users as admins. Setup given list of users as admins.
""" """
if not admins: os.makedirs(STATE_DIR, mode=0o700, exist_ok=True)
if not admin_password_list:
return return
logger.info("Setting up admin users") logger.info("Setting up admin users")
config_path = CONFIG_FILE config_path = CONFIG_FILE
@@ -286,9 +289,22 @@ def ensure_admins(admins):
config = {} config = {}
config['users'] = config.get('users', {}) config['users'] = config.get('users', {})
# Flatten admin lists
config['users']['admin'] = [admin for admin_sublist in admins db_passw = os.path.join(STATE_DIR, 'passwords.dbm')
for admin in admin_sublist]
admins = []
for admin_password_entry in admin_password_list:
for admin_password_pair in admin_password_entry:
if ":" in admin_password_pair:
admin, password = admin_password_pair.split(':')
admins.append(admin)
# Add admin:password to the db
password = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
with dbm.open(db_passw, 'c', 0o600) as db:
db[admin] = password
else:
admins.append(admin_password_pair)
config['users']['admin'] = admins
with open(config_path, 'w+') as f: with open(config_path, 'w+') as f:
yaml.dump(config, f) yaml.dump(config, f)