diff --git a/jupyterhub/app.py b/jupyterhub/app.py index d05b8923..fc624621 100644 --- a/jupyterhub/app.py +++ b/jupyterhub/app.py @@ -1960,7 +1960,7 @@ class JupyterHub(Application): user = orm.User.find(self.db, name=username) if user is None: if not self.authenticator.validate_username(username): - raise ValueError("Group username %r is not valid" % username) + raise ValueError("Username %r is not valid" % username) self.log.info(f"Creating user {username}") user = orm.User(name=username) self.db.add(user) @@ -2301,15 +2301,18 @@ class JupyterHub(Application): service.orm.server = None if service.oauth_available: + allowed_roles = [] + if service.oauth_roles: + allowed_roles = list( + self.db.query(orm.Role).filter( + orm.Role.name.in_(service.oauth_roles) + ) + ) oauth_client = self.oauth_provider.add_client( client_id=service.oauth_client_id, client_secret=service.api_token, redirect_uri=service.oauth_redirect_uri, - allowed_roles=list( - self.db.query(orm.Role).filter( - orm.Role.name.in_(service.oauth_roles) - ) - ), + allowed_roles=allowed_roles, description="JupyterHub service %s" % service.name, ) service.orm.oauth_client = oauth_client diff --git a/jupyterhub/orm.py b/jupyterhub/orm.py index 7dcf5a00..e9b94e80 100644 --- a/jupyterhub/orm.py +++ b/jupyterhub/orm.py @@ -717,7 +717,7 @@ class APIToken(Hashed, Base): db.add(orm_token) if not Role.find(db, 'token'): - raise AttributeError("Default token role has not been created") + raise RuntimeError("Default token role has not been created") try: if roles is not None: update_roles(db, entity=orm_token, roles=roles) diff --git a/jupyterhub/roles.py b/jupyterhub/roles.py index 23f220e4..32c92f15 100644 --- a/jupyterhub/roles.py +++ b/jupyterhub/roles.py @@ -183,9 +183,8 @@ def _get_subscopes(*roles, owner=None): scopes.update(role.scopes) expanded_scopes = set(chain.from_iterable(list(map(_expand_scope, scopes)))) - # transform !user filter to !user=ownername - for scope in expanded_scopes: + for scope in expanded_scopes.copy(): base_scope, _, filter = scope.partition('!') if filter == 'user': expanded_scopes.remove(scope) @@ -198,7 +197,6 @@ def _get_subscopes(*roles, owner=None): name = owner.name trans_scope = f'{base_scope}!user={name}' expanded_scopes.add(trans_scope) - if 'self' in expanded_scopes: expanded_scopes.remove('self') if owner and isinstance(owner, orm.User): @@ -232,7 +230,7 @@ def _check_scopes(*args, rolename=None): raise NameError(f"Scope '{scope}' {log_role} does not exist") if filter_: full_filter = f"!{filter_}" - if not any(full_filter in scope for full_filter in allowed_filters): + if not any(f in scope for f in allowed_filters): raise NameError( f"Scope filter '{full_filter}' in scope '{scope}' {log_role} does not exist" ) @@ -454,7 +452,8 @@ def assign_default_roles(db, entity): db.commit() # users and services can have 'user' or 'admin' roles as default else: - app_log.debug('Assigning default roles to %s', type(entity).__name__) + kind = type(entity).__name__ + app_log.debug(f'Assigning default roles to {kind} {entity.name}') _switch_default_role(db, entity, entity.admin) diff --git a/jupyterhub/tests/test_named_servers.py b/jupyterhub/tests/test_named_servers.py index e07f405f..8b9c4c04 100644 --- a/jupyterhub/tests/test_named_servers.py +++ b/jupyterhub/tests/test_named_servers.py @@ -143,7 +143,7 @@ async def test_delete_named_server(app, named_servers): username = 'donaar' user = add_user(app.db, app, name=username) assert user.allow_named_servers - cookies = app.login_user(username) + cookies = await app.login_user(username) servername = 'splugoth' r = await api_request(app, 'users', username, 'servers', servername, method='post') r.raise_for_status() diff --git a/jupyterhub/tests/test_roles.py b/jupyterhub/tests/test_roles.py index 7829719d..1ad5793c 100644 --- a/jupyterhub/tests/test_roles.py +++ b/jupyterhub/tests/test_roles.py @@ -788,6 +788,19 @@ async def test_user_filter_expansion(app, scope_list, kind, test_for_token): app.db.delete(test_role) +async def test_large_filter_expansion(app, create_temp_role, create_user_with_scopes): + scope_list = roles.expand_self_scope('==') + # Mimic the role 'self' based on '!user' filter for tokens + scope_list = [scope.rstrip("=") for scope in scope_list] + filtered_role = create_temp_role(scope_list) + user = create_user_with_scopes('self') + user.new_api_token(roles=[filtered_role.name]) + user.new_api_token(roles=['token']) + manual_scope_set = get_scopes_for(user.api_tokens[0]) + auto_scope_set = get_scopes_for(user.api_tokens[1]) + assert manual_scope_set == auto_scope_set + + @mark.role @mark.parametrize( "name, valid", @@ -849,6 +862,8 @@ async def test_server_role_api_calls( ): user = add_user(app.db, app, name='test_user') roles.grant_role(app.db, user, 'user') + app_log.debug(user.roles) + app_log.debug(roles.expand_roles_to_scopes(user.orm_user)) if token_role == 'no_role': api_token = user.new_api_token(roles=[]) else: