diff --git a/config/clusters/2i2c-aws-us/itcoocean.values.yaml b/config/clusters/2i2c-aws-us/itcoocean.values.yaml index e4ca3a6525..8a4f38feb1 100644 --- a/config/clusters/2i2c-aws-us/itcoocean.values.yaml +++ b/config/clusters/2i2c-aws-us/itcoocean.values.yaml @@ -248,7 +248,7 @@ jupyterhub: - display_name: "Bring your own image" description: Specify your own docker image (must have python and jupyterhub installed in it) slug: custom - allowed_teams: + allowed_groups: - Hackweek-ITCOocean:itcoocean-hackweek-2023 - nmfs-opensci:2i2c-demo - 2i2c-org:hub-access-for-2i2c-staff diff --git a/config/clusters/2i2c-aws-us/showcase.values.yaml b/config/clusters/2i2c-aws-us/showcase.values.yaml index 8809eff4d3..ef0d1d0644 100644 --- a/config/clusters/2i2c-aws-us/showcase.values.yaml +++ b/config/clusters/2i2c-aws-us/showcase.values.yaml @@ -58,7 +58,7 @@ basehub: profileList: - display_name: "Magic Link Demo" description: "For demoing magic links" - allowed_teams: + allowed_groups: - 2i2c-community-showcase:magiclinks-demo kubespawner_override: image: pangeo/pangeo-notebook:2023.06.20 @@ -69,7 +69,7 @@ basehub: node.kubernetes.io/instance-type: r5.xlarge - display_name: "NASA TOPS-T ScienceCore-ClimateRisk" description: "For collaborative work on 2i2c/MD's NASA TOPS-T ScienceCore Module" - allowed_teams: + allowed_groups: - 2i2c-demo-hub-access:showcase-topst - 2i2c-org:hub-access-for-2i2c-staff - ScienceCore:climaterisk-team @@ -82,7 +82,7 @@ basehub: node.kubernetes.io/instance-type: r5.xlarge - display_name: "NASA TOPS-T ScienceCore" description: "JupyterHubs for NASA ScienceCore Modules" - allowed_teams: + allowed_groups: - 2i2c-org:hub-access-for-2i2c-staff - ScienceCore:2i2c-showcase profile_options: @@ -132,7 +132,7 @@ basehub: node.kubernetes.io/instance-type: r5.xlarge - display_name: "Shared Small: 1-4 CPU, 8-32 GB" description: "A shared machine, the recommended option until you experience a limitation." - allowed_teams: &allowed_teams + allowed_groups: &allowed_groups - 2i2c-org:hub-access-for-2i2c-staff - 2i2c-community-showcase:access-2i2c-showcase profile_options: &profile_options @@ -188,7 +188,7 @@ basehub: - display_name: "Small: 4 CPU, 32 GB" description: "A dedicated machine for you." profile_options: *profile_options - allowed_teams: *allowed_teams + allowed_groups: *allowed_groups kubespawner_override: mem_guarantee: 28.937G cpu_guarantee: 0.4 @@ -199,7 +199,7 @@ basehub: - display_name: "Medium: 16 CPU, 128 GB" description: "A dedicated machine for you." profile_options: *profile_options - allowed_teams: *allowed_teams + allowed_groups: *allowed_groups kubespawner_override: mem_guarantee: 120.513G cpu_guarantee: 1.6 @@ -210,7 +210,7 @@ basehub: - display_name: "Large: 64 CPU, 512 GB" description: "A dedicated machine for you" profile_options: *profile_options - allowed_teams: *allowed_teams + allowed_groups: *allowed_groups kubespawner_override: mem_guarantee: 489.13G cpu_guarantee: 6.4 @@ -220,7 +220,7 @@ basehub: - display_name: NVIDIA Tesla T4, ~16 GB, ~4 CPUs slug: gpu - allowed_teams: + allowed_groups: - 2i2c-org:hub-access-for-2i2c-staff description: "Start a container on a dedicated node with a GPU" profile_options: diff --git a/config/clusters/earthscope/common.values.yaml b/config/clusters/earthscope/common.values.yaml index b1851b51b6..1cc15ddf43 100644 --- a/config/clusters/earthscope/common.values.yaml +++ b/config/clusters/earthscope/common.values.yaml @@ -98,87 +98,6 @@ basehub: c.GenericOAuthenticator.manage_groups = True c.JupyterHub.authenticator_class = CustomGenericOAuthenticator - - 002-group-profiles: | - # Filters profileList based on group membership of JupyterHubs - from copy import deepcopy - - from textwrap import dedent - from tornado import web - from functools import partial - - async def profile_list_allowed_groups_filter(original_profile_list, spawner): - """ - Returns the initially configured profile_list filtered based on the - user's membership in each profile's `allowed_groups`. If - `allowed_groups` isn't set for a profile, its not filtered out. - - `allowed_groups` is a list of JupyterHub groups. - - If the returned profile_list is filtered to not include a profile, - an error is raised and the user isn't allowed to start a server. - """ - if spawner.user.name == "deployment-service-check": - print("Ignoring allowed_teams check for deployment-service-check") - return original_profile_list - - groups = {g.name.casefold() for g in spawner.user.groups} - print(f"User {spawner.user.name} is part of groups {groups}") - - # Filter out profiles with allowed_groups set if the user isn't part of any. - allowed_profiles = [] - for orig_profile in original_profile_list: - profile = deepcopy(orig_profile) - if 'profile_options' in profile: - for k, po in profile['profile_options'].items(): - if 'choices' in po: - new_choices = {} - for k, c in po['choices'].items(): - if 'allowed_teams' not in c: - new_choices[k] = c - elif set(c['allowed_teams']) & groups: - new_choices[k] = c - po['choices'] = new_choices - - allowed_groups = set(profile.get("allowed_groups")) - - if allowed_groups is None: - # If no allowed_groups are set, allow access to everything - allowed_profiles.append(profile) - continue - - if allowed_groups & groups: - print(f"Allowing profile {profile['display_name']} for user {spawner.user.name} based on group membership") - allowed_profiles.append(profile) - continue - - if len(allowed_profiles) == 0: - # If no profiles are allowed, user should not be able to spawn anything! - # If we don't explicitly stop this, user will be logged into the 'default' settings - # set in singleuser, without any profile overrides. Not desired behavior - # FIXME: User doesn't actually see this error message, just the generic 403. - error_msg = dedent(f""" - Your Group team membership is insufficient to launch any server profiles. - - GitHub teams you are a member of that this JupyterHub knows about are {', '.join(groups)}. - - If you are part of additional teams, log out of this JupyterHub and log back in to refresh that information. - """) - raise web.HTTPError(403, error_msg) - - return allowed_profiles - - # Only set this customized profile_list *if* we already have a profile_list set - # otherwise, we'll show users a blank server options form and they won't be able to - # start their server - if c.KubeSpawner.profile_list: - # Customize list of profiles dynamically, rather than override options form. - # This is more secure, as users can't override the options available to them via the hub API - # We pass in a copy of the original profile_list set in config via partial, to reduce possible variable - # capture related issues. - c.KubeSpawner.profile_list = partial( - profile_list_allowed_groups_filter, deepcopy(c.KubeSpawner.profile_list) - ) config: # JupyterHub: # authenticator_class: auth0 diff --git a/config/clusters/leap/common.values.yaml b/config/clusters/leap/common.values.yaml index 44536212a7..1090165911 100644 --- a/config/clusters/leap/common.values.yaml +++ b/config/clusters/leap/common.values.yaml @@ -132,7 +132,7 @@ basehub: description: &profile_list_description "Start a container limited to a chosen share of capacity on a node of this type" slug: medium-full default: true - allowed_teams: + allowed_groups: - 2i2c-org:hub-access-for-2i2c-staff - leap-stc:leap-pangeo-full-access profile_options: @@ -235,13 +235,13 @@ basehub: # NOTE: This is the second medium profile list entry, with less node # share options for a different subset of users via the basehub - # specific allowed_teams configuration. + # specific allowed_groups configuration. # - display_name: "CPU only" description: *profile_list_description slug: medium-base default: true - allowed_teams: + allowed_groups: - leap-stc:leap-pangeo-base-access profile_options: requests: @@ -262,7 +262,7 @@ basehub: - display_name: GPU slug: gpu description: NVIDIA Tesla T4, 24GB RAM, 8 CPUs - allowed_teams: + allowed_groups: - 2i2c-org:hub-access-for-2i2c-staff - leap-stc:leap-pangeo-full-access profile_options: diff --git a/config/clusters/meom-ige/common.values.yaml b/config/clusters/meom-ige/common.values.yaml index f1edae137b..05fd095572 100644 --- a/config/clusters/meom-ige/common.values.yaml +++ b/config/clusters/meom-ige/common.values.yaml @@ -45,7 +45,7 @@ basehub: # # - display_name: Grenoble demo # default: true - # allowed_teams: + # allowed_groups: # - 2i2c-org:hub-access-for-2i2c-staff # - meom-group:hub-users # long term users # - demo-dask-grenoble2023:demo # temporary users for event @@ -93,7 +93,7 @@ basehub: # RAM on a node, not total node capacity - display_name: "Small" default: true - allowed_teams: &allowed_teams_normal_use + allowed_groups: &allowed_groups_normal_use - 2i2c-org:hub-access-for-2i2c-staff - meom-group:hub-users # long term users description: "~2 CPU, ~8G RAM" @@ -103,7 +103,7 @@ basehub: node_selector: node.kubernetes.io/instance-type: n1-standard-2 - display_name: "Medium" - allowed_teams: *allowed_teams_normal_use + allowed_groups: *allowed_groups_normal_use description: "~8 CPU, ~32G RAM" kubespawner_override: mem_limit: 32G @@ -111,7 +111,7 @@ basehub: node_selector: node.kubernetes.io/instance-type: n1-standard-8 - display_name: "Large" - allowed_teams: *allowed_teams_normal_use + allowed_groups: *allowed_groups_normal_use description: "~16 CPU, ~64G RAM" kubespawner_override: mem_limit: 64G @@ -119,7 +119,7 @@ basehub: node_selector: node.kubernetes.io/instance-type: n1-standard-16 - display_name: "Very Large" - allowed_teams: *allowed_teams_normal_use + allowed_groups: *allowed_groups_normal_use description: "~32 CPU, ~128G RAM" kubespawner_override: mem_limit: 128G @@ -127,7 +127,7 @@ basehub: node_selector: node.kubernetes.io/instance-type: n1-standard-32 - display_name: "Huge" - allowed_teams: *allowed_teams_normal_use + allowed_groups: *allowed_groups_normal_use description: "~64 CPU, ~256G RAM" kubespawner_override: mem_limit: 256G diff --git a/config/clusters/nasa-cryo/common.values.yaml b/config/clusters/nasa-cryo/common.values.yaml index 1f45fffe35..c0b578a146 100644 --- a/config/clusters/nasa-cryo/common.values.yaml +++ b/config/clusters/nasa-cryo/common.values.yaml @@ -119,7 +119,7 @@ basehub: description: &profile_list_description "Start a container with at least a chosen share of capacity on a node of this type" slug: small default: true - allowed_teams: + allowed_groups: - 2i2c-org:hub-access-for-2i2c-staff - CryoInTheCloud:cryoclouduser - CryoInTheCloud:cryocloudadvanced @@ -200,7 +200,7 @@ basehub: - display_name: "Medium: up to 16 CPU / 128 GB RAM" description: *profile_list_description slug: medium - allowed_teams: + allowed_groups: - 2i2c-org:hub-access-for-2i2c-staff - CryoInTheCloud:cryocloudadvanced - CryoInTheCloud:ml-in-glaciology @@ -268,7 +268,7 @@ basehub: # - display_name: "Large: up to 64 CPU / 512 GB RAM" # description: *profile_list_description # slug: large - # allowed_teams: + # allowed_groups: # - 2i2c-org:hub-access-for-2i2c-staff # - CryoInTheCloud:cryocloudadvanced # profile_options: @@ -328,7 +328,7 @@ basehub: - display_name: NVIDIA Tesla T4, ~16 GB, ~4 CPUs description: "Start a container on a dedicated node with a GPU" slug: "gpu" - allowed_teams: + allowed_groups: - 2i2c-org:hub-access-for-2i2c-staff - CryoInTheCloud:ml-in-glaciology profile_options: diff --git a/config/clusters/openscapes/common.values.yaml b/config/clusters/openscapes/common.values.yaml index 138242ec7a..ed41fb9779 100644 --- a/config/clusters/openscapes/common.values.yaml +++ b/config/clusters/openscapes/common.values.yaml @@ -45,7 +45,7 @@ basehub: - display_name: Python description: Python datascience environment default: true - allowed_teams: + allowed_groups: - 2i2c-org:hub-access-for-2i2c-staff - NASA-Openscapes:workshopaccess-2i2c # legacy but no plans to delete immediately until fledged - NASA-Openscapes:longtermaccess-2i2c @@ -128,7 +128,7 @@ basehub: node.kubernetes.io/instance-type: r5.4xlarge - display_name: R description: R (with RStudio) + Python environment - allowed_teams: + allowed_groups: - 2i2c-org:hub-access-for-2i2c-staff - NASA-Openscapes:workshopaccess-2i2c - NASA-Openscapes:longtermaccess-2i2c @@ -147,7 +147,7 @@ basehub: profile_options: *profile_options - display_name: Matlab description: Matlab environment - allowed_teams: + allowed_groups: - 2i2c-org:hub-access-for-2i2c-staff - NASA-Openscapes:workshopaccess-2i2c - NASA-Openscapes:longtermaccess-2i2c @@ -164,7 +164,7 @@ basehub: - display_name: "Bring your own image" description: Specify your own docker image (must have python and jupyterhub installed in it) slug: custom - allowed_teams: + allowed_groups: - NASA-Openscapes:longtermaccess-2i2c - 2i2c-org:hub-access-for-2i2c-staff # Requested in: https://2i2c.freshdesk.com/a/tickets/1284 diff --git a/config/clusters/pangeo-hubs/common.values.yaml b/config/clusters/pangeo-hubs/common.values.yaml index 96543bb828..3bb38ef5c4 100644 --- a/config/clusters/pangeo-hubs/common.values.yaml +++ b/config/clusters/pangeo-hubs/common.values.yaml @@ -62,7 +62,7 @@ basehub: - display_name: "Small" description: 5GB RAM, 2 CPUs default: true - allowed_teams: + allowed_groups: - pangeo-data:us-central1-b-gcp - 2i2c-org:hub-access-for-2i2c-staff kubespawner_override: @@ -72,7 +72,7 @@ basehub: node.kubernetes.io/instance-type: n1-standard-2 - display_name: Medium description: 11GB RAM, 4 CPUs - allowed_teams: + allowed_groups: - pangeo-data:us-central1-b-gcp - 2i2c-org:hub-access-for-2i2c-staff kubespawner_override: @@ -82,7 +82,7 @@ basehub: node.kubernetes.io/instance-type: n1-standard-4 - display_name: Large description: 24GB RAM, 8 CPUs - allowed_teams: + allowed_groups: - pangeo-data:cds-lab - 2i2c-org:hub-access-for-2i2c-staff kubespawner_override: @@ -92,7 +92,7 @@ basehub: node.kubernetes.io/instance-type: n1-standard-8 - display_name: Huge description: 52GB RAM, 16 CPUs - allowed_teams: + allowed_groups: - pangeo-data:cds-lab - 2i2c-org:hub-access-for-2i2c-staff kubespawner_override: diff --git a/config/clusters/smithsonian/common.values.yaml b/config/clusters/smithsonian/common.values.yaml index a33202123a..b6ee632298 100644 --- a/config/clusters/smithsonian/common.values.yaml +++ b/config/clusters/smithsonian/common.values.yaml @@ -168,7 +168,7 @@ basehub: - display_name: NVIDIA Tesla T4, ~16 GB, ~4 CPUs slug: gpu description: "Start a container on a dedicated node with a GPU" - allowed_teams: + allowed_groups: - 2i2c-org:hub-access-for-2i2c-staff - Smithsonian-SDCH:gpu-users profile_options: diff --git a/docs/howto/features/allow-unlisted-profile-choice.md b/docs/howto/features/allow-unlisted-profile-choice.md index 85b48d60a9..81f5dc929c 100644 --- a/docs/howto/features/allow-unlisted-profile-choice.md +++ b/docs/howto/features/allow-unlisted-profile-choice.md @@ -38,7 +38,7 @@ jupyterhub: In some hubs, we don't want *everyone* to be able to specify an image - but we do want some subset of users to be able to do so, for testing purposes. This can be done by coupling `unlisted_choice` with -[`allowed_teams`](auth:github-orgs:profile-list). +[`allowed_groups`](auth:github-orgs:profile-list). In the `profileList` for the hub in question, add a profile like this: @@ -46,7 +46,7 @@ In the `profileList` for the hub in question, add a profile like this: - display_name: "Test custom image" description: Test any custom image before rolling it out to rest of your users slug: custom-image-only - allowed_teams: + allowed_groups: - 2i2c-org:hub-access-for-2i2c-staff - profile_options: diff --git a/docs/hub-deployment-guide/configure-auth/github-orgs.md b/docs/hub-deployment-guide/configure-auth/github-orgs.md index e02161950a..84fce892b4 100644 --- a/docs/hub-deployment-guide/configure-auth/github-orgs.md +++ b/docs/hub-deployment-guide/configure-auth/github-orgs.md @@ -143,7 +143,7 @@ In addition, we can allow people access to specific profiles based on their GitH This only works if the hub is already set to allow people only from certain GitHub organizations to log in. -The key `allowed_teams` can be set for any profile definition, with a list of GitHub +The key `allowed_groups` can be set for any profile definition, with a list of GitHub teams (formatted as `:`) that will get access to that profile. Users need to be a member of any one of the listed teams for access. The list of teams a user is part of is fetched at login time - so if the user is added to a GitHub team, they need @@ -171,7 +171,7 @@ To enable this access, If `populate_teams_in_auth_state` is not set, this entire feature is disabled. 2. Specify which teams should have access to which profiles with an - `allowed_teams` key under `profileList`: + `allowed_groups` key under `profileList`: ```yaml jupyterhub: @@ -180,7 +180,7 @@ To enable this access, - display_name: Small description: 1.0 GB RAM default: true - allowed_teams: + allowed_groups: - : - 2i2c-org:hub-access-for-2i2c-staff kubespawner_override: @@ -188,7 +188,7 @@ To enable this access, mem_limit: 1G - display_name: Medium description: 4.0 GB RAM - allowed_teams: + allowed_groups: - : - 2i2c-org:hub-access-for-2i2c-staff kubespawner_override: @@ -198,8 +198,8 @@ To enable this access, Users who are a part of *any* of the listed teams will be able to access that profile. Add `2i2c-org:hub-access-for-2i2c-staff` to all - `allowed_teams` so 2i2c engineers can log in to debug issues. If - `allowed_teams` is not set, that profile is not available to anyone. + `allowed_groups` so 2i2c engineers can log in to debug issues. If + `allowed_groups` is not set, that profile is not available to anyone. ```{note} We used to allow restricting which profiles users can see based on what diff --git a/helm-charts/basehub/values.yaml b/helm-charts/basehub/values.yaml index 871d789877..65e41cba90 100644 --- a/helm-charts/basehub/values.yaml +++ b/helm-charts/basehub/values.yaml @@ -980,10 +980,10 @@ jupyterhub: timeout=spawner.k8s_api_request_retry_timeout ) c.Spawner.pre_spawn_hook = ensure_db_pvc - 05-gh-teams: | + 05-profile-groups: | # Re-assignes c.KubeSpawner.profile_list to a callable that filters the # initial configuration of profile_list based on the user's github - # org/team membership as declared via "allowed_teams" read from + # org/team membership as declared via "allowed_groups" read from # profile_list profiles. # # This only has effect if: @@ -1001,80 +1001,84 @@ jupyterhub: from oauthenticator.github import GitHubOAuthenticator from z2jh import get_config - async def profile_list_allowed_teams_filter(original_profile_list, spawner): + async def profile_list_allowed_groups_filter(original_profile_list, spawner): """ Returns the initially configured profile_list filtered based on the - user's membership in each profile's `allowed_teams`. If - `allowed_teams` isn't set for a profile, its not filtered out. + user's membership in each profile's `allowed_groups`. If + `allowed_groups` isn't set for a profile, that profile is allowed for + everyone. Similar functionality is provided for both `unlisted_choice` and + `choice` inside `profile_options`. - `allowed_teams` is a list of GitHub organizations and/or teams - specified with `` or `:` strings. + `allowed_groups` is a list of JupyterHub groups, set up by the authenticator. + In addition, for use with GitHubOAuthenticator, it can be a list of + teams the user is a part of, of form ':'. - If the returned profile_list is filtered to not include a profile, + If the returned profile_list is filtered to not include any profiles, an error is raised and the user isn't allowed to start a server. """ if spawner.user.name == "deployment-service-check": - print("Ignoring allowed_teams check for deployment-service-check") + print("Ignoring allowed_groups check for deployment-service-check") return original_profile_list - # Ensure auth_state is populated with teams info - auth_state = await spawner.user.get_auth_state() - if not auth_state or "teams" not in auth_state: - print(f"User {spawner.user.name} does not have any auth_state set") - raise web.HTTPError(403) + groups = {g.name.casefold() for g in spawner.user.groups} - # Format user's teams in auth_state to "org:team" - # casefold them so we can do case insensitive comparisons, as github itself is case insensitive (but preserving) - # for orgs and teams - teams = set([f'{team["organization"]["login"]}:{team["slug"]}'.casefold() for team in auth_state["teams"]]) - print(f"User {spawner.user.name} is part of teams {' '.join(teams)}") + # If we're using GitHubOAuthenticator, add the user's teams to the groups as well. + # Eventually this can be removed, as the user's teams can be set to be groups + # once https://github.com/jupyterhub/oauthenticator/pull/735 is merged + if isinstance(spawner.authenticator, GitHubOAuthenticator): + # Ensure auth_state is populated with teams info + auth_state = await spawner.user.get_auth_state() + if not auth_state or "teams" not in auth_state: + print(f"User {spawner.user.name} does not have any auth_state set") + raise web.HTTPError(403) - # Filter out profiles with allowed_teams set if the user isn't part - # of any. + groups |= set([f'{team["organization"]["login"]}:{team["slug"]}'.casefold() for team in auth_state["teams"]]) + + print(f"User {spawner.user.name} is part of groups {' '.join(groups)}") + + # Filter out profiles with allowed_groups set if the user isn't part of the group allowed_profiles = [] for original_profile in original_profile_list: # Make a copy, as we'll be modifying this profile profile = deepcopy(original_profile) - # Handle `allowed_teams` specified in profile_options + # Handle `allowed_groups` specified in profile_options if 'profile_options' in profile: for k, po in profile['profile_options'].items(): - # If `unlisted_choice` has an `allowed_teams` and the current + # If `unlisted_choice` has an `allowed_groups` and the current # user is not present in any of those teams, we delete the # `unlisted_choice` config entirely for this option. The user # will then not be allowed to 'write in' a value. if 'unlisted_choice' in po: - if 'allowed_teams' in po['unlisted_choice']: - if (set(po['unlisted_choice']['allowed_teams']) and teams): + if 'allowed_groups' in po['unlisted_choice']: + if not (set(po['unlisted_choice']['allowed_groups']) and groups): del po['unlisted_choice'] if 'choices' in po: new_choices = {} for k, c in po['choices'].items(): - # If `allowed_teams` is not set for a profile option, it is automatically + # If `allowed_groups` is not set for a profile option, it is automatically # allowed for everyone - if 'allowed_teams' not in c: + if 'allowed_groups' not in c: new_choices[k] = c - # If `allowed_teams` *is* set for a profile option, it is allowed only for + # If `allowed_groups` *is* set for a profile option, it is allowed only for # members of that team. - elif set(c['allowed_teams']) & teams: + elif set(c['allowed_groups']) & groups: new_choices[k] = c po['choices'] = new_choices - allowed_teams = profile.get("allowed_teams") - if allowed_teams is None: - allowed_profiles.append(profile) - continue - # casefold teams so we can do case insensitive comparisons, as github itself is case insensitive (but preserving) # for orgs and teams - allowed_teams = set([t.casefold() for t in allowed_teams if ':' in t]) + if 'allowed_groups' not in profile: + allowed_profiles.append(profile) + else: + allowed_groups = set([g.casefold() for g in profile.get("allowed_groups", [])]) - if allowed_teams & teams: - print(f"Allowing profile {profile['display_name']} for user {spawner.user.name} based on team membership") - allowed_profiles.append(profile) - continue + if allowed_groups & groups: + print(f"Allowing profile {profile['display_name']} for user {spawner.user.name} based on team membership") + allowed_profiles.append(profile) + continue if len(allowed_profiles) == 0: # If no profiles are allowed, user should not be able to spawn anything! @@ -1082,37 +1086,25 @@ jupyterhub: # set in singleuser, without any profile overrides. Not desired behavior # FIXME: User doesn't actually see this error message, just the generic 403. error_msg = dedent(f""" - Your GitHub team membership is insufficient to launch any server profiles. + Your JupyterHub group membership is insufficient to launch any server profiles. - GitHub teams you are a member of that this JupyterHub knows about are {', '.join(teams)}. + JupyterHub groups you are a member of are {', '.join(groups)}. - If you are part of additional teams, log out of this JupyterHub and log back in to refresh that information. + If you are part of additional groups, log out of this JupyterHub and log back in to refresh that information. """) raise web.HTTPError(403, error_msg) return allowed_profiles - # Check if GitHubOAuthenticator is being used, via one of the 4 ways it can be specified - is_github_auth = c.JupyterHub.authenticator_class in ( - "github", "oauthenticator.GitHubOAuthenticator", "oauthenticator.github.GitHubOAuthenticator", - GitHubOAuthenticator) - - # Check that teams are going to be populated in auth_state - is_teams_populated = c.GitHubOAuthenticator.populate_teams_in_auth_state and ( - c.Authenticator.enable_auth_state or c.GitHubOAuthenticator.enable_auth_state - ) - - # Only use this customized profile_list function if: - # 1. We are using GitHubOAuthenticator - # 2. GitHub teams membership is populated in auth_state - # 3. profile_list is specified (otherwise users will get an empty screen when trying to launch servers) - if is_github_auth and is_teams_populated and c.KubeSpawner.profile_list: + # Only set our custom filter if + # profile_list is specified (otherwise users will get an empty screen when trying to launch servers) + if c.KubeSpawner.profile_list: # Customize list of profiles dynamically, rather than override options form. # This is more secure, as users can't override the options available to them via the hub API # We pass in a copy of the original profile_list set in config via partial, to reduce possible variable # capture related issues. c.KubeSpawner.profile_list = partial( - profile_list_allowed_teams_filter, + profile_list_allowed_groups_filter, deepcopy(c.KubeSpawner.profile_list) )