From e9a7f2aaca6da6e468673ec8c1ef8a9fbddc284e Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Fri, 5 Apr 2024 16:54:28 -0700 Subject: [PATCH 1/2] gh: Allow setting JupyterHub groups from GitHub teams Looking at https://github.com/jupyterhub/oauthenticator/pull/735, I realize it will not actually allow us to use GitHub teams or orgs as JupyterHub groups, since that's an extra API call. This PR keeps it simple, and adds config to pick this up from teams list directly. --- oauthenticator/github.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/oauthenticator/github.py b/oauthenticator/github.py index fcc63b3b..2d76bec5 100644 --- a/oauthenticator/github.py +++ b/oauthenticator/github.py @@ -126,7 +126,7 @@ def _userdata_url_default(self): https://docs.github.com/en/rest/reference/teams#list-teams-for-the-authenticated-user. Requires `read:org` to be set in `scope`. - + Note that authentication state is only be available to a `post_auth_hook` before being discarded unless configured to be persisted via `enable_auth_state`. For more information, see @@ -134,6 +134,18 @@ def _userdata_url_default(self): """, ) + populate_groups_from_teams = Bool( + False, + config=True, + help=""" + Populates JupyterHub groups from list of GitHub teams the user is a part of. + + Requires `read:org` to be set in `scope`. + + Requires `manage_groups` to be set to True. + """ + ) + # _deprecated_oauth_aliases is used by deprecation logic in OAuthenticator _deprecated_oauth_aliases = { "github_client_id": ("client_id", "0.1.0"), @@ -224,6 +236,9 @@ async def update_auth_model(self, auth_model): user_info["email"] = val["email"] break + + # https://docs.github.com/en/rest/teams/teams?apiVersion=2022-11-28#list-teams-for-the-authenticated-user + teams_url = f"{self.github_api}/user/teams?per_page=100" if self.populate_teams_in_auth_state: if "read:org" not in self.scope: # This means the "read:org" scope was not set, and we can't @@ -232,13 +247,22 @@ async def update_auth_model(self, auth_model): "read:org scope is required for populate_teams_in_auth_state functionality to work" ) else: - # https://docs.github.com/en/rest/teams/teams?apiVersion=2022-11-28#list-teams-for-the-authenticated-user - url = f"{self.github_api}/user/teams?per_page=100" - user_teams = await self._paginated_fetch(url, access_token, token_type) + user_teams = await self._paginated_fetch(teams_url, access_token, token_type) auth_model["auth_state"]["teams"] = user_teams + if self.manage_groups and self.populate_groups_from_teams: + if "read:org" not in self.scope: + # This means the "read:org" scope was not set, and we can't fetch teams + self.log.error( + "read:org scope is required for populate_groups_from_teams functionality to work" + ) + else: + user_teams = await self._paginated_fetch(teams_url, access_token, token_type) + auth_model["groups"] = user_teams + return auth_model + async def _paginated_fetch(self, api_url, access_token, token_type): """ Fetch all items via a paginated GitHub API call From 70e170662cfdbf8fe4414c499a7b789318cd0aaa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 5 Apr 2024 23:57:22 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- oauthenticator/github.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/oauthenticator/github.py b/oauthenticator/github.py index 2d76bec5..cd6e2cc3 100644 --- a/oauthenticator/github.py +++ b/oauthenticator/github.py @@ -143,7 +143,7 @@ def _userdata_url_default(self): Requires `read:org` to be set in `scope`. Requires `manage_groups` to be set to True. - """ + """, ) # _deprecated_oauth_aliases is used by deprecation logic in OAuthenticator @@ -236,7 +236,6 @@ async def update_auth_model(self, auth_model): user_info["email"] = val["email"] break - # https://docs.github.com/en/rest/teams/teams?apiVersion=2022-11-28#list-teams-for-the-authenticated-user teams_url = f"{self.github_api}/user/teams?per_page=100" if self.populate_teams_in_auth_state: @@ -247,7 +246,9 @@ async def update_auth_model(self, auth_model): "read:org scope is required for populate_teams_in_auth_state functionality to work" ) else: - user_teams = await self._paginated_fetch(teams_url, access_token, token_type) + user_teams = await self._paginated_fetch( + teams_url, access_token, token_type + ) auth_model["auth_state"]["teams"] = user_teams if self.manage_groups and self.populate_groups_from_teams: @@ -257,12 +258,13 @@ async def update_auth_model(self, auth_model): "read:org scope is required for populate_groups_from_teams functionality to work" ) else: - user_teams = await self._paginated_fetch(teams_url, access_token, token_type) + user_teams = await self._paginated_fetch( + teams_url, access_token, token_type + ) auth_model["groups"] = user_teams return auth_model - async def _paginated_fetch(self, api_url, access_token, token_type): """ Fetch all items via a paginated GitHub API call