Skip to content

feat(sharing): default user-role share scopes and Keycloak group sync - #189

Open
tylerpotts wants to merge 2 commits into
mainfrom
fix-app-sharing-defaults
Open

feat(sharing): default user-role share scopes and Keycloak group sync#189
tylerpotts wants to merge 2 commits into
mainfrom
fix-app-sharing-defaults

Conversation

@tylerpotts

Copy link
Copy Markdown
Contributor

Fixes #188.

Problem

The jhub-apps "Individuals and group access" dropdown is empty for every user — admins included — on stock deployments of this chart. jhub-apps computes the dropdown as (other hub users + hub groups) filtered by the requesting user's token scopes (read:users:name!user=X / read:groups:name!group=G); verified on a live deployment by expanding real tokens, even admin's token is self-scoped. And /hub/api/groups is always [] because nothing syncs Keycloak groups into JupyterHub. Full diagnosis in #188.

Change (values-only)

  1. hub.loadRoles.user — extends the user role with read:users:name, read:groups:name, shares!user, exactly what jhub-apps' reference jupyterhub_config.py prescribes for sharing. self is repeated because naming a role in load_roles replaces its default definition. Deployers opt out by overriding the role to scopes: [self] — the values comment documents that read:users:name implies username enumeration, which is inherent to sharing by name.
  2. hub.config.KeyCloakOAuthenticator.manage_groups: true — standard oauthenticator trait mirroring each user's Keycloak groups into JupyterHub groups at login. Inert under the default dummy authenticator; the pack's KeyCloakOAuthenticator.update_auth_model defers to super() first, so the groups claim flows through untouched. A group becomes a share target once one of its members has logged in.

Both are plain z2jh values, so deployer overrides merge naturally — no new toggle machinery.

Verification

Note for reviewers

After upgrade, users must log in again once — scopes attach to tokens minted at login, and group membership syncs at login.

jhub-apps builds the 'Individuals and group access' dropdown from
(other hub users + hub groups) filtered by the requesting user's token
scopes. Stock deployments of this chart shipped neither half:
JupyterHub's default user role is self-only (so even admins resolve an
empty visible-users list), and nothing mirrors Keycloak groups into
JupyterHub, so the group list is always empty. Net effect: the sharing
feature was dead on every deployment.

- hub.loadRoles.user: extend the user role with read:users:name,
  read:groups:name, shares!user - the scopes jhub-apps' reference
  jupyterhub_config.py prescribes. Deployers opt out by overriding the
  role to scopes: [self] (read:users:name implies username enumeration,
  inherent to sharing by name; documented in the values comment).
- hub.config.KeyCloakOAuthenticator.manage_groups: true - standard
  oauthenticator trait, inert under the dummy authenticator; the
  pack's KeyCloakOAuthenticator.update_auth_model defers to super()
  so the groups claim flows through untouched.

Fixes #188
@tylerpotts
tylerpotts requested review from aktech and pmeier as code owners July 21, 2026 15:43
install_jhub_apps appends its own 'user' role to load_roles, and
z2jh's hub.loadRoles loop runs before config.d - the values route
produces two roles named user and JupyterHub aborts startup with
'Role user multiply defined' (caught on a live deployment). Extend
the existing role's scopes right after install_jhub_apps instead,
the pattern jhub-apps' reference config uses, gated on
custom.sharing-scopes-enabled (default true).
@tylerpotts

Copy link
Copy Markdown
Contributor Author

Heads-up for reviewers: the first commit's hub.loadRoles approach was wrong and is corrected in the second commit. install_jhub_apps appends its own user role to load_roles, and z2jh's hub.loadRoles loop runs before the config.d files — so the values route produces two roles named user and JupyterHub aborts startup with ValueError: Role user multiply defined (caught the hard way on a live deployment).

The fix now extends the existing role's scopes in place right after install_jhub_apps in 02-jhub-apps.py — the pattern jhub-apps' own reference jupyterhub_config.py uses — gated on jupyterhub.custom.sharing-scopes-enabled (default true), which also gives the opt-out toggle #188 asked about. manage_groups stays values-based (no collision there). Resulting user role scopes: self, access:services, list:services, admin:auth_state (jhub-apps' own) + read:users:name, read:groups:name, shares!user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

jhub-apps share dropdown (Individuals and group access) is always empty: no user-role read scopes, no manage_groups

2 participants