mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-18 15:33:02 +00:00
Fixed a bug, added some docs, but running into DB/API issues
This commit is contained in:
@@ -56,6 +56,7 @@ class RootAPIHandler(APIHandler):
|
|||||||
def get(self):
|
def get(self):
|
||||||
"""GET /api/ returns info about the Hub and its API.
|
"""GET /api/ returns info about the Hub and its API.
|
||||||
|
|
||||||
|
It is not an authenticated endpoint
|
||||||
For now, it just returns the version of JupyterHub itself.
|
For now, it just returns the version of JupyterHub itself.
|
||||||
"""
|
"""
|
||||||
data = {'version': __version__}
|
data = {'version': __version__}
|
||||||
@@ -67,7 +68,8 @@ class InfoAPIHandler(APIHandler):
|
|||||||
def get(self):
|
def get(self):
|
||||||
"""GET /api/info returns detailed info about the Hub and its API.
|
"""GET /api/info returns detailed info about the Hub and its API.
|
||||||
|
|
||||||
Currently, it returns information on the python version, spawner and authenticator
|
Currently, it returns information on the python version, spawner and authenticator.
|
||||||
|
Since this information might be sensitive, it is an authenticated endpoint
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _class_info(typ):
|
def _class_info(typ):
|
||||||
|
@@ -33,7 +33,7 @@ class ServiceListAPIHandler(APIHandler):
|
|||||||
def get(self, scope_filter=None):
|
def get(self, scope_filter=None):
|
||||||
data = {name: service_model(service) for name, service in self.services.items()}
|
data = {name: service_model(service) for name, service in self.services.items()}
|
||||||
if scope_filter is not None:
|
if scope_filter is not None:
|
||||||
data = dict(filter(lambda tup: tup[0] in scope_filter))
|
data = dict(filter(lambda tup: tup[0] in scope_filter, data.items()))
|
||||||
self.write(json.dumps(data))
|
self.write(json.dumps(data))
|
||||||
|
|
||||||
|
|
||||||
|
@@ -68,9 +68,9 @@ def _get_scope_filter(db, req_scope, sub_scope):
|
|||||||
if req_scope not in scope_translator:
|
if req_scope not in scope_translator:
|
||||||
raise AttributeError("Scope not found; scope filter not constructed")
|
raise AttributeError("Scope not found; scope filter not constructed")
|
||||||
kind = scope_translator[req_scope]
|
kind = scope_translator[req_scope]
|
||||||
Class = orm.get_class(kind)
|
Resource = orm.get_class(kind)
|
||||||
sub_scope_values = next(iter(sub_scope.values()))
|
sub_scope_values = next(iter(sub_scope.values()))
|
||||||
query = db.query(Class).filter(Class.name.in_(sub_scope_values))
|
query = db.query(Resource).filter(Resource.name.in_(sub_scope_values))
|
||||||
scope_filter = {entry.name for entry in query.all()}
|
scope_filter = {entry.name for entry in query.all()}
|
||||||
if 'group' in sub_scope and kind == 'users':
|
if 'group' in sub_scope and kind == 'users':
|
||||||
groups = orm.Group.name.in_(sub_scope['group'])
|
groups = orm.Group.name.in_(sub_scope['group'])
|
||||||
@@ -164,7 +164,6 @@ def needs_scope(scope):
|
|||||||
if 'scope_filter' in bound_sig.arguments:
|
if 'scope_filter' in bound_sig.arguments:
|
||||||
s_kwargs['scope_filter'] = None
|
s_kwargs['scope_filter'] = None
|
||||||
if 'all' in self.scopes and self.current_user:
|
if 'all' in self.scopes and self.current_user:
|
||||||
# todo: What if no user is found? See test_api/test_referer_check
|
|
||||||
self.scopes |= get_user_scopes(self.current_user.name)
|
self.scopes |= get_user_scopes(self.current_user.name)
|
||||||
parsed_scopes = _parse_scopes(self.scopes)
|
parsed_scopes = _parse_scopes(self.scopes)
|
||||||
scope_filter = _check_scope(self, scope, parsed_scopes, **s_kwargs)
|
scope_filter = _check_scope(self, scope, parsed_scopes, **s_kwargs)
|
||||||
|
@@ -13,9 +13,11 @@ from ..scopes import _check_scope
|
|||||||
from ..scopes import _parse_scopes
|
from ..scopes import _parse_scopes
|
||||||
from ..scopes import needs_scope
|
from ..scopes import needs_scope
|
||||||
from ..scopes import Scope
|
from ..scopes import Scope
|
||||||
|
from .mocking import MockHub
|
||||||
from .utils import add_user
|
from .utils import add_user
|
||||||
from .utils import api_request
|
from .utils import api_request
|
||||||
from .utils import auth_header
|
from .utils import auth_header
|
||||||
|
from .utils import public_url
|
||||||
|
|
||||||
|
|
||||||
def test_scope_constructor():
|
def test_scope_constructor():
|
||||||
@@ -125,6 +127,7 @@ class MockAPIHandler:
|
|||||||
(['read:users'], 'user_thing', ('gob',), False),
|
(['read:users'], 'user_thing', ('gob',), False),
|
||||||
(['read:users'], 'user_thing', ('michael',), False),
|
(['read:users'], 'user_thing', ('michael',), False),
|
||||||
(['users!user=george'], 'user_thing', ('george',), True),
|
(['users!user=george'], 'user_thing', ('george',), True),
|
||||||
|
(['users!user=george'], 'user_thing', ('fake_user',), False),
|
||||||
(['users!user=george'], 'user_thing', ('oscar',), False),
|
(['users!user=george'], 'user_thing', ('oscar',), False),
|
||||||
(['users!user=george', 'users!user=oscar'], 'user_thing', ('oscar',), True),
|
(['users!user=george', 'users!user=oscar'], 'user_thing', ('oscar',), True),
|
||||||
(['users:servers'], 'server_thing', ('user1', 'server_1'), True),
|
(['users:servers'], 'server_thing', ('user1', 'server_1'), True),
|
||||||
@@ -198,6 +201,16 @@ def test_double_scoped_method_denials():
|
|||||||
obj.secret_thing()
|
obj.secret_thing()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_test_role(user_name, scopes, role_name='test'):
|
||||||
|
role = {
|
||||||
|
'name': role_name,
|
||||||
|
'description': '',
|
||||||
|
'users': [user_name],
|
||||||
|
'scopes': scopes,
|
||||||
|
}
|
||||||
|
return role
|
||||||
|
|
||||||
|
|
||||||
@mark.parametrize(
|
@mark.parametrize(
|
||||||
"user_name, in_group, status_code",
|
"user_name, in_group, status_code",
|
||||||
[
|
[
|
||||||
@@ -238,19 +251,28 @@ async def test_expand_groups(app, user_name, in_group, status_code):
|
|||||||
assert r.status_code == status_code
|
assert r.status_code == status_code
|
||||||
|
|
||||||
|
|
||||||
|
async def test_non_existing_user(app):
|
||||||
|
user_name = 'shade'
|
||||||
|
user = add_user(app.db, name=user_name)
|
||||||
|
app.db.commit()
|
||||||
|
app.db.delete(user)
|
||||||
|
app.db.commit()
|
||||||
|
print(app.db.query(orm.User).all()) # no shade
|
||||||
|
r = await api_request(app, 'users', headers=auth_header(app.db, user_name))
|
||||||
|
print(r.json()) # shade
|
||||||
|
# Fixme: no shade in db, user models still throw shade
|
||||||
|
assert r.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
async def test_user_filter(app):
|
async def test_user_filter(app):
|
||||||
user_name = 'rita'
|
user_name = 'rita'
|
||||||
test_role = {
|
user = add_user(app.db, name=user_name)
|
||||||
'name': 'test',
|
app.db.commit()
|
||||||
'description': '',
|
scopes = ['read:users!user=lindsay', 'read:users!user=gob', 'read:users!user=oscar']
|
||||||
'users': [user_name],
|
test_role = generate_test_role(user, scopes)
|
||||||
'scopes': [
|
|
||||||
'read:users!user=lindsay',
|
|
||||||
'read:users!user=gob',
|
|
||||||
'read:users!user=oscar',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
roles.add_role(app.db, test_role)
|
roles.add_role(app.db, test_role)
|
||||||
|
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test')
|
||||||
|
roles.remove_obj(app.db, objname=user_name, kind='users', rolename='user')
|
||||||
name_in_scope = {'lindsay', 'oscar', 'gob'}
|
name_in_scope = {'lindsay', 'oscar', 'gob'}
|
||||||
outside_scope = {'maeby', 'marta'}
|
outside_scope = {'maeby', 'marta'}
|
||||||
group_name = 'bluth'
|
group_name = 'bluth'
|
||||||
@@ -262,10 +284,6 @@ async def test_user_filter(app):
|
|||||||
user = add_user(app.db, name=name)
|
user = add_user(app.db, name=name)
|
||||||
if name not in group.users:
|
if name not in group.users:
|
||||||
group.users.append(user)
|
group.users.append(user)
|
||||||
kind = 'users'
|
|
||||||
user = add_user(app.db, name=user_name)
|
|
||||||
roles.update_roles(app.db, user, kind, roles=['test'])
|
|
||||||
roles.remove_obj(app.db, user_name, kind, 'user')
|
|
||||||
app.db.commit()
|
app.db.commit()
|
||||||
r = await api_request(app, 'users', headers=auth_header(app.db, user_name))
|
r = await api_request(app, 'users', headers=auth_header(app.db, user_name))
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
@@ -273,16 +291,44 @@ async def test_user_filter(app):
|
|||||||
assert result_names == name_in_scope
|
assert result_names == name_in_scope
|
||||||
|
|
||||||
|
|
||||||
async def test_user_filter_with_group(app): # todo: Move role setup to setup method
|
async def test_service_filter(app):
|
||||||
user_name = 'sally' # Fixme: fails randomly? scopes not always loaded?
|
services = [
|
||||||
test_role = {
|
{'name': 'cull_idle', 'api_token': 'some-token'},
|
||||||
'name': 'test',
|
{'name': 'user_service', 'api_token': 'some-other-token'},
|
||||||
'description': '',
|
]
|
||||||
'users': [user_name],
|
for service in app.db.query(orm.Service):
|
||||||
'scopes': ['read:users!group=sitwell'],
|
app.db.delete(service)
|
||||||
}
|
for service in services:
|
||||||
roles.add_role(app.db, test_role)
|
orm_service = orm.Service.find(app.db, name=service['name'])
|
||||||
|
if orm_service is None:
|
||||||
|
# not found, create a new one
|
||||||
|
orm_service = orm.Service(name=service['name'])
|
||||||
|
app.db.add(orm_service)
|
||||||
|
app.db.commit()
|
||||||
|
app.init_services()
|
||||||
|
user_name = 'buster'
|
||||||
user = add_user(app.db, name=user_name)
|
user = add_user(app.db, name=user_name)
|
||||||
|
app.db.commit()
|
||||||
|
test_role = generate_test_role(user, ['read:services!service=cull_idle'])
|
||||||
|
roles.add_role(app.db, test_role)
|
||||||
|
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test')
|
||||||
|
r = await api_request(app, 'services', headers=auth_header(app.db, user_name))
|
||||||
|
assert r.status_code == 200
|
||||||
|
result_names = {service['name'] for service in r.json()}
|
||||||
|
# Fixme: Again DB/API sync issue
|
||||||
|
assert result_names == {'culler'}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_filter_with_group(app):
|
||||||
|
# Move role setup to setup method?
|
||||||
|
user_name = 'sally'
|
||||||
|
add_user(app.db, name=user_name)
|
||||||
|
external_user_name = 'britta'
|
||||||
|
add_user(app.db, name=external_user_name)
|
||||||
|
test_role = generate_test_role(user_name, ['read:users!group=sitwell'])
|
||||||
|
roles.add_role(app.db, test_role)
|
||||||
|
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test')
|
||||||
|
|
||||||
name_set = {'sally', 'stan'}
|
name_set = {'sally', 'stan'}
|
||||||
group_name = 'sitwell'
|
group_name = 'sitwell'
|
||||||
group = orm.Group.find(app.db, name=group_name)
|
group = orm.Group.find(app.db, name=group_name)
|
||||||
@@ -293,40 +339,31 @@ async def test_user_filter_with_group(app): # todo: Move role setup to setup me
|
|||||||
user = add_user(app.db, name=name)
|
user = add_user(app.db, name=name)
|
||||||
if name not in group.users:
|
if name not in group.users:
|
||||||
group.users.append(user)
|
group.users.append(user)
|
||||||
kind = 'users'
|
|
||||||
roles.update_roles(app.db, user, kind, roles=['test'])
|
|
||||||
roles.remove_obj(app.db, user_name, kind, 'user')
|
|
||||||
app.db.commit()
|
app.db.commit()
|
||||||
|
|
||||||
r = await api_request(app, 'users', headers=auth_header(app.db, user_name))
|
r = await api_request(app, 'users', headers=auth_header(app.db, user_name))
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
result_names = {user['name'] for user in r.json()}
|
result_names = {user['name'] for user in r.json()}
|
||||||
assert result_names == name_set
|
assert result_names == name_set
|
||||||
|
assert external_user_name not in result_names
|
||||||
|
|
||||||
|
|
||||||
async def test_group_scope_filter(app):
|
async def test_group_scope_filter(app):
|
||||||
user_name = 'rollerblade'
|
user_name = 'rollerblade'
|
||||||
test_role = {
|
add_user(app.db, name=user_name)
|
||||||
'name': 'test',
|
scopes = ['read:groups!group=sitwell', 'read:groups!group=bluth']
|
||||||
'description': '',
|
test_role = generate_test_role(user_name, scopes)
|
||||||
'users': [user_name],
|
|
||||||
'scopes': [
|
|
||||||
'read:groups!group=sitwell',
|
|
||||||
'read:groups!group=bluths',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
roles.add_role(app.db, test_role)
|
roles.add_role(app.db, test_role)
|
||||||
user = add_user(app.db, name=user_name)
|
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test')
|
||||||
group_set = {'sitwell', 'bluths', 'austero'}
|
|
||||||
|
group_set = {'sitwell', 'bluth', 'austero'}
|
||||||
for group_name in group_set:
|
for group_name in group_set:
|
||||||
group = orm.Group.find(app.db, name=group_name)
|
group = orm.Group.find(app.db, name=group_name)
|
||||||
if not group:
|
if not group:
|
||||||
group = orm.Group(name=group_name)
|
group = orm.Group(name=group_name)
|
||||||
app.db.add(group)
|
app.db.add(group)
|
||||||
kind = 'users'
|
|
||||||
roles.update_roles(app.db, user, kind, roles=['test'])
|
|
||||||
roles.remove_obj(app.db, user_name, kind, 'user')
|
|
||||||
app.db.commit()
|
app.db.commit()
|
||||||
r = await api_request(app, 'groups', headers=auth_header(app.db, user_name))
|
r = await api_request(app, 'groups', headers=auth_header(app.db, user_name))
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
result_names = {user['name'] for user in r.json()}
|
result_names = {user['name'] for user in r.json()}
|
||||||
assert result_names == {'sitwell', 'bluths'}
|
assert result_names == {'sitwell', 'bluth'}
|
||||||
|
Reference in New Issue
Block a user