diff --git a/.claude/JOURNAL.md b/.claude/JOURNAL.md
index 1ac9286..388d83d 100644
--- a/.claude/JOURNAL.md
+++ b/.claude/JOURNAL.md
@@ -147,3 +147,6 @@ This journal tracks substantive work on documents, diagrams, and documentation c
48. **Task - Settings dictionary YAML**: Externalized settings metadata to config/settings_dictionary.yml
**Result**: Created settings_dictionary.yml with categories as top-level keys (JupyterHub Core, Docker Spawner, GPU, Services, Idle Culler, Branding), each containing list of settings with name, description, default, and optional empty_display. Updated SettingsPageHandler to load from YAML instead of hardcoded values. Added pyyaml to Dockerfile pip install. Dockerfile now copies settings_dictionary.yml to /srv/jupyterhub/
+
+49. **Task - xkcdpass password generation**: Replaced custom word list with xkcdpass library for auto-generated passwords
+ **Result**: Moved settings_dictionary.yml to services/jupyterhub/conf/ for proper image baking. Replaced hardcoded word list with xkcdpass library for memorable password generation. Added configurable env vars: JUPYTERHUB_AUTOGENERATED_PASSWORD_WORDS (default 4) and JUPYTERHUB_AUTOGENERATED_PASSWORD_DELIMITER (default "-"). Added xkcdpass to Dockerfile pip install. Fixed ENABLE_SIGNUP to JUPYTERHUB_SIGNUP_ENABLED in Dockerfile defaults
diff --git a/config/jupyterhub_config.py b/config/jupyterhub_config.py
index 4b05dc0..337421b 100644
--- a/config/jupyterhub_config.py
+++ b/config/jupyterhub_config.py
@@ -88,11 +88,11 @@ def sync_nativeauth_on_rename(target, value, oldvalue, initiator):
@event.listens_for(orm.User, 'after_insert')
def create_nativeauth_on_user_insert(mapper, connection, target):
"""Auto-create NativeAuthenticator UserInfo when a new User is created via admin panel.
- Generates a memorable password and auto-approves the user."""
+ Generates a memorable password using xkcdpass and auto-approves the user."""
username = target.name
try:
import bcrypt
- import random
+ from xkcdpass import xkcd_password as xp
from sqlalchemy import text
# Check if UserInfo already exists (user might have signed up normally)
@@ -104,11 +104,12 @@ def create_nativeauth_on_user_insert(mapper, connection, target):
print(f"[NativeAuth Auto-Create] UserInfo already exists for: {username}")
return
- # Generate memorable 3-word password
- words = ['apple', 'beach', 'cloud', 'dance', 'eagle', 'flame', 'grape', 'happy',
- 'ivory', 'jolly', 'karma', 'lemon', 'mango', 'noble', 'ocean', 'piano',
- 'quest', 'river', 'storm', 'tiger', 'urban', 'vivid', 'water', 'zebra']
- password = '-'.join(random.sample(words, 3))
+ # Generate memorable password using xkcdpass (configurable via env)
+ num_words = int(os.environ.get('JUPYTERHUB_AUTOGENERATED_PASSWORD_WORDS', 4))
+ delimiter = os.environ.get('JUPYTERHUB_AUTOGENERATED_PASSWORD_DELIMITER', '-')
+ wordfile = xp.locate_wordfile()
+ words = xp.generate_wordlist(wordfile=wordfile, min_length=4, max_length=6)
+ password = xp.generate_xkcdpassword(words, numwords=num_words, delimiter=delimiter)
# Hash password with bcrypt
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
diff --git a/services/jupyterhub/Dockerfile.jupyterhub b/services/jupyterhub/Dockerfile.jupyterhub
index 1ae0d8b..c8f80de 100644
--- a/services/jupyterhub/Dockerfile.jupyterhub
+++ b/services/jupyterhub/Dockerfile.jupyterhub
@@ -42,9 +42,9 @@ COPY --chmod=600 services/jupyterhub/templates/certs /mnt/certs
COPY --chmod=644 services/jupyterhub/templates_enhanced/*.html /srv/jupyterhub/templates/
COPY --chmod=644 services/jupyterhub/templates_enhanced/static/custom.css /tmp/custom.css
COPY --chmod=644 config/jupyterhub_config.py /srv/jupyterhub/jupyterhub_config.py
-COPY --chmod=644 config/settings_dictionary.yml /srv/jupyterhub/settings_dictionary.yml
+COPY --chmod=644 services/jupyterhub/conf/settings_dictionary.yml /srv/jupyterhub/settings_dictionary.yml
-## install dockerspawner, nativeauthenticator, idle-culler, pyyaml
+## install dockerspawner, nativeauthenticator, idle-culler, pyyaml, xkcdpass
RUN <<-EOF
echo "installing core jupyterhub python packages"
pip install -U --no-cache-dir \
@@ -52,7 +52,8 @@ RUN <<-EOF
dockerspawner \
jupyterhub-nativeauthenticator \
jupyterhub-idle-culler \
- pyyaml
+ pyyaml \
+ xkcdpass
EOF
## copy custom.css to JupyterHub's static directory
@@ -62,7 +63,9 @@ RUN <<-EOF
EOF
## default environment variables
-ENV ENABLE_SIGNUP=1
+ENV JUPYTERHUB_SIGNUP_ENABLED=1
+ENV JUPYTERHUB_AUTOGENERATED_PASSWORD_WORDS=4
+ENV JUPYTERHUB_AUTOGENERATED_PASSWORD_DELIMITER="-"
ENV STELLARS_JUPYTERHUB_VERSION=${VERSION}
ENV JUPYTERHUB_CUSTOM_LOGO_URI=""
diff --git a/config/settings_dictionary.yml b/services/jupyterhub/conf/settings_dictionary.yml
similarity index 88%
rename from config/settings_dictionary.yml
rename to services/jupyterhub/conf/settings_dictionary.yml
index aed5b2f..b33b9e1 100644
--- a/config/settings_dictionary.yml
+++ b/services/jupyterhub/conf/settings_dictionary.yml
@@ -20,6 +20,14 @@ JupyterHub Core:
description: SSL/TLS (0=disabled, 1=enabled)
default: "1"
+ - name: JUPYTERHUB_AUTOGENERATED_PASSWORD_WORDS
+ description: Number of words in auto-generated passwords
+ default: "4"
+
+ - name: JUPYTERHUB_AUTOGENERATED_PASSWORD_DELIMITER
+ description: Delimiter between words in auto-generated passwords
+ default: "-"
+
Docker Spawner:
- name: JUPYTERHUB_NOTEBOOK_IMAGE
description: User container image