diff --git a/oauthenticator/generic.py b/oauthenticator/generic.py index 31945d6a..4dd88bb6 100644 --- a/oauthenticator/generic.py +++ b/oauthenticator/generic.py @@ -27,8 +27,8 @@ def _login_service_default(self): that accepts the returned json (as a dict) and returns the groups list. This configures how group membership in the upstream provider is determined - for use by `allowed_groups`, `admin_groups`, etc. - It has no effect on its own, and is not related to users' _JupyterHub_ group membership. + for use by `allowed_groups`, `admin_groups`, etc. If `manage_groups` is True, + this will also determine users' _JupyterHub_ group membership. """, ) @@ -154,15 +154,22 @@ async def update_auth_model(self, auth_model): the user isn't part of `admin_users` or `admin_groups`. Note that leaving it at None makes users able to retain an admin status while setting it to False makes it be revoked. + + Also populates groups if `manage_groups` is set. """ + if self.manage_groups or self.admin_groups: + user_info = auth_model["auth_state"][self.user_auth_state_key] + user_groups = self.get_user_groups(user_info) + + if self.manage_groups: + auth_model["groups"] = sorted(user_groups) + if auth_model["admin"]: # auth_model["admin"] being True means the user was in admin_users return auth_model if self.admin_groups: # admin status should in this case be True or False, not None - user_info = auth_model["auth_state"][self.user_auth_state_key] - user_groups = self.get_user_groups(user_info) auth_model["admin"] = bool(user_groups & self.admin_groups) return auth_model diff --git a/oauthenticator/tests/test_generic.py b/oauthenticator/tests/test_generic.py index 42dc7781..fcc96652 100644 --- a/oauthenticator/tests/test_generic.py +++ b/oauthenticator/tests/test_generic.py @@ -151,6 +151,15 @@ def get_authenticator(generic_client): False, False, ), + ( + "20", + { + "manage_groups": True, + "allow_all": True, + }, + True, + None, + ), ], ) async def test_generic( @@ -166,6 +175,9 @@ async def test_generic( c.GenericOAuthenticator = Config(class_config) c.GenericOAuthenticator.username_claim = "username" authenticator = get_authenticator(config=c) + manage_groups = False + if "manage_groups" in class_config: + manage_groups = authenticator.manage_groups handled_user_model = user_model("user1") handler = generic_client.handler_for_user(handled_user_model) @@ -173,7 +185,10 @@ async def test_generic( if expect_allowed: assert auth_model - assert set(auth_model) == {"name", "admin", "auth_state"} + expected_keys = {"name", "admin", "auth_state"} + if manage_groups: + expected_keys.add("groups") + assert set(auth_model) == expected_keys assert auth_model["admin"] == expect_admin auth_state = auth_model["auth_state"] assert json.dumps(auth_state) @@ -183,6 +198,9 @@ async def test_generic( assert "scope" in auth_state user_info = auth_state[authenticator.user_auth_state_key] assert auth_model["name"] == user_info[authenticator.username_claim] + if manage_groups: + assert auth_model["groups"] == user_info[authenticator.claim_groups_key] + else: assert auth_model == None