Refactored role methods

This commit is contained in:
0mar
2021-03-29 21:26:34 +02:00
parent 036a4eb934
commit 1515747b1e
12 changed files with 148 additions and 138 deletions

View File

@@ -14,6 +14,7 @@ from tornado import web
from tornado.iostream import StreamClosedError from tornado.iostream import StreamClosedError
from .. import orm from .. import orm
from ..roles import assign_default_roles
from ..roles import update_roles from ..roles import update_roles
from ..scopes import needs_scope from ..scopes import needs_scope
from ..user import User from ..user import User
@@ -151,7 +152,7 @@ class UserListAPIHandler(APIHandler):
user = self.user_from_username(name) user = self.user_from_username(name)
if admin: if admin:
user.admin = True user.admin = True
update_roles(self.db, obj=user, kind='users') assign_default_roles(self.db, entity=user)
self.db.commit() self.db.commit()
try: try:
await maybe_future(self.authenticator.add_user(user)) await maybe_future(self.authenticator.add_user(user))
@@ -218,7 +219,7 @@ class UserAPIHandler(APIHandler):
self._check_user_model(data) self._check_user_model(data)
if 'admin' in data: if 'admin' in data:
user.admin = data['admin'] user.admin = data['admin']
update_roles(self.db, obj=user, kind='users') assign_default_roles(self.db, entity=user)
self.db.commit() self.db.commit()
try: try:
@@ -286,7 +287,7 @@ class UserAPIHandler(APIHandler):
else: else:
setattr(user, key, value) setattr(user, key, value)
if key == 'admin': if key == 'admin':
update_roles(self.db, obj=user, kind='users') assign_default_roles(self.db, entity=user)
self.db.commit() self.db.commit()
user_ = self.user_model(user) user_ = self.user_model(user)
user_['auth_state'] = await user.get_auth_state() user_['auth_state'] = await user.get_auth_state()

View File

@@ -1857,15 +1857,16 @@ class JupyterHub(Application):
# load default roles # load default roles
default_roles = roles.get_default_roles() default_roles = roles.get_default_roles()
for role in default_roles: for role in default_roles:
roles.add_role(db, role) roles.create_role(db, role)
# load predefined roles from config file # load predefined roles from config file
for predef_role in self.load_roles: for predef_role in self.load_roles:
roles.add_role(db, predef_role) roles.create_role(db, predef_role)
# add users, services and/or tokens # add users, services and/or tokens
for bearer in role_bearers: for bearer in role_bearers:
if bearer in predef_role.keys(): if bearer in predef_role.keys():
for bname in predef_role[bearer]: for bname in predef_role[bearer]:
if bearer == 'users': if bearer == 'users':
bname = self.authenticator.normalize_username(bname) bname = self.authenticator.normalize_username(bname)
if not ( if not (
@@ -1877,8 +1878,10 @@ class JupyterHub(Application):
"Username %r is not in Authenticator.allowed_users" "Username %r is not in Authenticator.allowed_users"
% bname % bname
) )
roles.add_obj( Class = orm.get_class(bearer)
db, objname=bname, kind=bearer, rolename=predef_role['name'] orm_obj = Class.find(db, bname)
roles.grant_role(
db, entity=orm_obj, rolename=predef_role['name']
) )
# make sure all users, services and tokens have at least one role (update with default) # make sure all users, services and tokens have at least one role (update with default)
@@ -1886,7 +1889,7 @@ class JupyterHub(Application):
Class = orm.get_class(bearer) Class = orm.get_class(bearer)
for obj in db.query(Class): for obj in db.query(Class):
if len(obj.roles) < 1: if len(obj.roles) < 1:
roles.update_roles(db, obj=obj, kind=bearer) roles.assign_default_roles(db, entity=obj)
db.commit() db.commit()
async def _add_tokens(self, token_dict, kind): async def _add_tokens(self, token_dict, kind):

View File

@@ -480,7 +480,7 @@ class BaseHandler(RequestHandler):
# not found, create and register user # not found, create and register user
u = orm.User(name=username) u = orm.User(name=username)
self.db.add(u) self.db.add(u)
roles.update_roles(self.db, obj=u, kind='users') roles.assign_default_roles(self.db, entity=u)
TOTAL_USERS.inc() TOTAL_USERS.inc()
self.db.commit() self.db.commit()
user = self._user_from_orm(u) user = self._user_from_orm(u)
@@ -765,7 +765,7 @@ class BaseHandler(RequestHandler):
# Only set `admin` if the authenticator returned an explicit value. # Only set `admin` if the authenticator returned an explicit value.
if admin is not None and admin != user.admin: if admin is not None and admin != user.admin:
user.admin = admin user.admin = admin
roles.update_roles(self.db, obj=user, kind='users') roles.assign_default_roles(self.db, entity=user)
self.db.commit() self.db.commit()
# always set auth_state and commit, # always set auth_state and commit,
# because there could be key-rotation or clearing of previous values # because there could be key-rotation or clearing of previous values

View File

@@ -39,7 +39,8 @@ from sqlalchemy.types import Text
from sqlalchemy.types import TypeDecorator from sqlalchemy.types import TypeDecorator
from tornado.log import app_log from tornado.log import app_log
from .roles import add_role from .roles import assign_default_roles
from .roles import create_role
from .roles import get_default_roles from .roles import get_default_roles
from .roles import update_roles from .roles import update_roles
from .utils import compare_token from .utils import compare_token
@@ -621,8 +622,12 @@ class APIToken(Hashed, Base):
if not token_role: if not token_role:
default_roles = get_default_roles() default_roles = get_default_roles()
for role in default_roles: for role in default_roles:
add_role(db, role) create_role(db, role)
update_roles(db, obj=orm_token, kind='tokens', roles=roles) if roles:
update_roles(db, entity=orm_token, roles=roles)
else:
assign_default_roles(db, entity=orm_token)
db.commit() db.commit()
return token return token

View File

@@ -73,7 +73,7 @@ def expand_self_scope(name, read_only=False):
return {"{}!user={}".format(scope, name) for scope in scope_list} return {"{}!user={}".format(scope, name) for scope in scope_list}
def get_scope_hierarchy(): def _get_scope_hierarchy():
""" """
Returns a dictionary of scopes: Returns a dictionary of scopes:
scopes.keys() = scopes of highest level and scopes that have their own subscopes scopes.keys() = scopes of highest level and scopes that have their own subscopes
@@ -106,7 +106,7 @@ def get_scope_hierarchy():
def expand_scope(scopename): def expand_scope(scopename):
"""Returns a set of all subscopes""" """Returns a set of all subscopes"""
scopes = get_scope_hierarchy() scopes = _get_scope_hierarchy()
subscopes = [scopename] subscopes = [scopename]
def expand_subscopes(index): def expand_subscopes(index):
@@ -133,7 +133,7 @@ def expand_scope(scopename):
def expand_roles_to_scopes(orm_object): def expand_roles_to_scopes(orm_object):
"""Get the scopes listed in the roles of the User/Service/Group/Token""" """Get the scopes listed in the roles of the User/Service/Group/Token"""
scopes = get_subscopes(*orm_object.roles) scopes = _get_subscopes(*orm_object.roles)
if 'self' in scopes: if 'self' in scopes:
scopes.remove('self') scopes.remove('self')
if isinstance(orm_object, orm.User) or hasattr(orm_object, 'orm_user'): if isinstance(orm_object, orm.User) or hasattr(orm_object, 'orm_user'):
@@ -141,7 +141,7 @@ def expand_roles_to_scopes(orm_object):
return scopes return scopes
def get_subscopes(*args): def _get_subscopes(*args):
"""Returns a set of all available subscopes for a specified role or list of roles""" """Returns a set of all available subscopes for a specified role or list of roles"""
scope_list = [] scope_list = []
@@ -154,7 +154,7 @@ def get_subscopes(*args):
return scopes return scopes
def add_role(db, role_dict): def create_role(db, role_dict):
"""Adds a new role to database or modifies an existing one""" """Adds a new role to database or modifies an existing one"""
if 'name' not in role_dict.keys(): if 'name' not in role_dict.keys():
@@ -180,105 +180,103 @@ def add_role(db, role_dict):
def existing_only(func): def existing_only(func):
"""Decorator for checking if objects and roles exist""" """Decorator for checking if objects and roles exist"""
def check_existence(db, objname, kind, rolename): def check_existence(db, entity, rolename):
Class = orm.get_class(kind)
obj = Class.find(db, objname)
role = orm.Role.find(db, rolename) role = orm.Role.find(db, rolename)
if entity is None:
if obj is None: raise ValueError(
raise ValueError("%r of kind %r does not exist" % (objname, kind)) "%r of kind %r does not exist" % (entity, type(entity).__name__)
)
elif role is None: elif role is None:
raise ValueError("Role %r does not exist" % rolename) raise ValueError("Role %r does not exist" % rolename)
else: else:
func(db, obj, kind, role) func(db, entity, role)
return check_existence return check_existence
@existing_only @existing_only
def add_obj(db, objname, kind, rolename): def grant_role(db, entity, rolename):
"""Adds a role for users, services or tokens""" """Adds a role for users, services or tokens"""
if rolename not in entity.roles:
if rolename not in objname.roles: entity.roles.append(rolename)
objname.roles.append(rolename)
db.commit() db.commit()
@existing_only @existing_only
def remove_obj(db, objname, kind, rolename): def strip_role(db, entity, rolename):
"""Removes a role for users, services or tokens""" """Removes a role for users, services or tokens"""
if rolename in entity.roles:
if rolename in objname.roles: entity.roles.remove(rolename)
objname.roles.remove(rolename)
db.commit() db.commit()
def switch_default_role(db, obj, kind, admin): def _switch_default_role(db, obj, admin):
"""Switch between default user/service and admin roles for users/services""" """Switch between default user/service and admin roles for users/services"""
user_role = orm.Role.find(db, 'user') user_role = orm.Role.find(db, 'user')
admin_role = orm.Role.find(db, 'admin') admin_role = orm.Role.find(db, 'admin')
def add_and_remove(db, obj, kind, current_role, new_role): def add_and_remove(db, obj, current_role, new_role):
if current_role in obj.roles: if current_role in obj.roles:
remove_obj(db, objname=obj.name, kind=kind, rolename=current_role.name) strip_role(db, entity=obj, rolename=current_role.name)
# only add new default role if the user has no other roles # only add new default role if the user has no other roles
if len(obj.roles) < 1: if len(obj.roles) < 1:
add_obj(db, objname=obj.name, kind=kind, rolename=new_role.name) grant_role(db, entity=obj, rolename=new_role.name)
if admin: if admin:
add_and_remove(db, obj, kind, user_role, admin_role) add_and_remove(db, obj, user_role, admin_role)
else: else:
add_and_remove(db, obj, kind, admin_role, user_role) add_and_remove(db, obj, admin_role, user_role)
def update_roles(db, obj, kind, roles=None): def assign_default_roles(db, entity):
"""Assigns the default roles to an entity:
users and services get 'user' role, unless they have admin flag
Tokens get 'token' role"""
default_token_role = orm.Role.find(db, 'token')
# tokens can have only 'token' role as default
# assign the default only for tokens
if isinstance(entity, orm.APIToken):
if not entity.roles and entity.user is not None:
default_token_role.tokens.append(entity)
db.commit()
# users and services can have 'user' or 'admin' roles as default
else:
# todo: when we deprecate admin flag: replace with role check
_switch_default_role(db, entity, entity.admin)
def update_roles(db, entity, roles):
"""Updates object's roles if specified, """Updates object's roles if specified,
assigns default if no roles specified""" assigns default if no roles specified"""
Class = type(entity)
Class = orm.get_class(kind)
default_token_role = orm.Role.find(db, 'token')
standard_permissions = {'all', 'read:all'} standard_permissions = {'all', 'read:all'}
if roles: for rolename in roles:
for rolename in roles:
if Class == orm.APIToken:
role = orm.Role.find(db, rolename)
if role:
# compare the requested role permissions with the owner's permissions (scopes)
token_scopes = get_subscopes(role)
extra_scopes = token_scopes - standard_permissions
# find the owner and their roles
owner = None
if obj.user_id:
owner = db.query(orm.User).get(obj.user_id)
elif obj.service_id:
owner = db.query(orm.Service).get(obj.service_id)
if owner:
owner_scopes = expand_roles_to_scopes(owner)
if (extra_scopes).issubset(owner_scopes):
role.tokens.append(obj)
else:
raise ValueError(
'Requested token role %r has more permissions than the token owner: [%s]'
% (rolename, ",".join(extra_scopes - owner_scopes))
)
else:
raise NameError('Role %r does not exist' % rolename)
else:
add_obj(db, objname=obj.name, kind=kind, rolename=rolename)
else:
# tokens can have only 'token' role as default
# assign the default only for tokens
if Class == orm.APIToken: if Class == orm.APIToken:
if not obj.roles and obj.user is not None:
default_token_role.tokens.append(obj) role = orm.Role.find(db, rolename)
db.commit() if role:
# users and services can have 'user' or 'admin' roles as default # compare the requested role permissions with the owner's permissions (scopes)
token_scopes = _get_subscopes(role)
extra_scopes = token_scopes - standard_permissions
# find the owner and their roles
owner = None
if entity.user_id:
owner = db.query(orm.User).get(entity.user_id)
elif entity.service_id:
owner = db.query(orm.Service).get(entity.service_id)
if owner:
owner_scopes = expand_roles_to_scopes(owner)
if (extra_scopes).issubset(owner_scopes):
role.tokens.append(entity)
else:
raise ValueError(
'Requested token role %r has more permissions than the token owner: [%s]'
% (rolename, ",".join(extra_scopes - owner_scopes))
)
else:
raise NameError('Role %r does not exist' % rolename)
else: else:
switch_default_role(db, obj, kind, obj.admin) grant_role(db, entity=entity, rolename=rolename)
def mock_roles(app, name, kind): def mock_roles(app, name, kind):
@@ -287,5 +285,5 @@ def mock_roles(app, name, kind):
obj = Class.find(app.db, name=name) obj = Class.find(app.db, name=name)
default_roles = get_default_roles() default_roles = get_default_roles()
for role in default_roles: for role in default_roles:
add_role(app.db, role) create_role(app.db, role)
update_roles(db=app.db, obj=obj, kind=kind) assign_default_roles(db=app.db, entity=obj)

View File

@@ -251,7 +251,7 @@ def _mockservice(request, app, url=False):
assert name in app._service_map assert name in app._service_map
service = app._service_map[name] service = app._service_map[name]
token = service.orm.api_tokens[0] token = service.orm.api_tokens[0]
update_roles(app.db, token, 'tokens', roles=['token']) update_roles(app.db, token, roles=['token'])
async def start(): async def start():
# wait for proxy to be updated before starting the service # wait for proxy to be updated before starting the service

View File

@@ -342,7 +342,7 @@ class MockHub(JupyterHub):
self.db.add(user) self.db.add(user)
self.db.commit() self.db.commit()
metrics.TOTAL_USERS.inc() metrics.TOTAL_USERS.inc()
roles.update_roles(self.db, obj=user, kind='users') roles.assign_default_roles(self.db, entity=user)
self.db.commit() self.db.commit()
def stop(self): def stop(self):

View File

@@ -50,7 +50,7 @@ def test_raise_error_on_missing_specified_config():
process = Popen( process = Popen(
[sys.executable, '-m', 'jupyterhub', '--config', 'not-available.py'] [sys.executable, '-m', 'jupyterhub', '--config', 'not-available.py']
) )
# wait inpatiently for the process to exit like we want it to # wait impatiently for the process to exit like we want it to
for i in range(100): for i in range(100):
time.sleep(0.1) time.sleep(0.1)
returncode = process.poll() returncode = process.poll()

View File

@@ -180,9 +180,9 @@ def test_orm_roles_delete_cascade(db):
) )
def test_get_subscopes(db, scopes, subscopes): def test_get_subscopes(db, scopes, subscopes):
"""Test role scopes expansion into their subscopes""" """Test role scopes expansion into their subscopes"""
roles.add_role(db, {'name': 'testing_scopes', 'scopes': scopes}) roles.create_role(db, {'name': 'testing_scopes', 'scopes': scopes})
role = orm.Role.find(db, name='testing_scopes') role = orm.Role.find(db, name='testing_scopes')
response = roles.get_subscopes(role) response = roles._get_subscopes(role)
assert response == subscopes assert response == subscopes
db.delete(role) db.delete(role)
@@ -386,8 +386,8 @@ async def test_load_roles_tokens(tmpdir, request):
) )
async def test_get_new_token_via_api(app, headers, role_list, status): async def test_get_new_token_via_api(app, headers, role_list, status):
user = add_user(app.db, app, name='user') user = add_user(app.db, app, name='user')
roles.add_role(app.db, {'name': 'reader', 'scopes': ['all']}) roles.create_role(app.db, {'name': 'reader', 'scopes': ['all']})
roles.add_role(app.db, {'name': 'user_creator', 'scopes': ['admin:users']}) roles.create_role(app.db, {'name': 'user_creator', 'scopes': ['admin:users']})
if role_list: if role_list:
body = json.dumps({'roles': role_list}) body = json.dumps({'roles': role_list})
else: else:

View File

@@ -230,7 +230,7 @@ async def test_expand_groups(app, user_name, in_group, status_code):
'read:groups', 'read:groups',
], ],
} }
roles.add_role(app.db, test_role) roles.create_role(app.db, test_role)
user = add_user(app.db, name=user_name) user = add_user(app.db, name=user_name)
group_name = 'bluth' group_name = 'bluth'
group = orm.Group.find(app.db, name=group_name) group = orm.Group.find(app.db, name=group_name)
@@ -240,8 +240,8 @@ async def test_expand_groups(app, user_name, in_group, status_code):
if in_group and user not in group.users: if in_group and user not in group.users:
group.users.append(user) group.users.append(user)
kind = 'users' kind = 'users'
roles.update_roles(app.db, user, kind, roles=['test']) roles.update_roles(app.db, user, roles=['test'])
roles.remove_obj(app.db, user_name, kind, 'user') roles.strip_role(app.db, user, 'user')
app.db.commit() app.db.commit()
r = await api_request( r = await api_request(
app, 'users', user_name, headers=auth_header(app.db, user_name) app, 'users', user_name, headers=auth_header(app.db, user_name)
@@ -265,10 +265,10 @@ err_message = "No access to resources or resources not found"
async def test_request_fake_user(app): async def test_request_fake_user(app):
user_name = 'buster' user_name = 'buster'
fake_user = 'annyong' fake_user = 'annyong'
add_user(app.db, name=user_name) user = add_user(app.db, name=user_name)
test_role = generate_test_role(user_name, ['read:users!group=stuff']) test_role = generate_test_role(user_name, ['read:users!group=stuff'])
roles.add_role(app.db, test_role) roles.create_role(app.db, test_role)
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test') roles.grant_role(app.db, entity=user, rolename='test')
app.db.commit() app.db.commit()
r = await api_request( r = await api_request(
app, 'users', fake_user, headers=auth_header(app.db, user_name) app, 'users', fake_user, headers=auth_header(app.db, user_name)
@@ -284,8 +284,8 @@ async def test_refuse_exceeding_token_permissions(app):
add_user(app.db, name='user') add_user(app.db, name='user')
api_token = user.new_api_token() api_token = user.new_api_token()
exceeding_role = generate_test_role(user_name, ['read:users'], 'exceeding_role') exceeding_role = generate_test_role(user_name, ['read:users'], 'exceeding_role')
roles.add_role(app.db, exceeding_role) roles.create_role(app.db, exceeding_role)
roles.add_obj(app.db, objname=api_token, kind='tokens', rolename='exceeding_role') roles.grant_role(app.db, entity=user.api_tokens[0], rolename='exceeding_role')
app.db.commit() app.db.commit()
headers = {'Authorization': 'token %s' % api_token} headers = {'Authorization': 'token %s' % api_token}
r = await api_request(app, 'users', headers=headers) r = await api_request(app, 'users', headers=headers)
@@ -304,11 +304,11 @@ async def test_exceeding_user_permissions(app):
subreader_role = generate_test_role( subreader_role = generate_test_role(
user_name, ['read:users:groups'], 'subreader_role' user_name, ['read:users:groups'], 'subreader_role'
) )
roles.add_role(app.db, reader_role) roles.create_role(app.db, reader_role)
roles.add_role(app.db, subreader_role) roles.create_role(app.db, subreader_role)
app.db.commit() app.db.commit()
roles.update_roles(app.db, user, kind='users', roles=['reader_role']) roles.update_roles(app.db, user, roles=['reader_role'])
roles.update_roles(app.db, orm_api_token, kind='tokens', roles=['subreader_role']) roles.update_roles(app.db, orm_api_token, roles=['subreader_role'])
orm_api_token.roles.remove(orm.Role.find(app.db, name='token')) orm_api_token.roles.remove(orm.Role.find(app.db, name='token'))
app.db.commit() app.db.commit()
@@ -318,7 +318,7 @@ async def test_exceeding_user_permissions(app):
keys = {key for user in r.json() for key in user.keys()} keys = {key for user in r.json() for key in user.keys()}
assert 'groups' in keys assert 'groups' in keys
assert 'last_activity' not in keys assert 'last_activity' not in keys
roles.remove_obj(app.db, user_name, 'users', 'reader_role') roles.strip_role(app.db, user, 'reader_role')
async def test_user_service_separation(app, mockservice_url): async def test_user_service_separation(app, mockservice_url):
@@ -327,13 +327,11 @@ async def test_user_service_separation(app, mockservice_url):
reader_role = generate_test_role(name, ['read:users'], 'reader_role') reader_role = generate_test_role(name, ['read:users'], 'reader_role')
subreader_role = generate_test_role(name, ['read:users:groups'], 'subreader_role') subreader_role = generate_test_role(name, ['read:users:groups'], 'subreader_role')
roles.add_role(app.db, reader_role) roles.create_role(app.db, reader_role)
roles.add_role(app.db, subreader_role) roles.create_role(app.db, subreader_role)
app.db.commit() app.db.commit()
roles.update_roles(app.db, user, kind='users', roles=['subreader_role']) roles.update_roles(app.db, user, roles=['subreader_role'])
roles.update_roles( roles.update_roles(app.db, mockservice_url.orm, roles=['reader_role'])
app.db, mockservice_url.orm, kind='services', roles=['reader_role']
)
user.roles.remove(orm.Role.find(app.db, name='user')) user.roles.remove(orm.Role.find(app.db, name='user'))
api_token = user.new_api_token() api_token = user.new_api_token()
app.db.commit() app.db.commit()
@@ -348,12 +346,12 @@ async def test_user_service_separation(app, mockservice_url):
async def test_request_user_outside_group(app): async def test_request_user_outside_group(app):
user_name = 'buster' user_name = 'buster'
fake_user = 'hello' fake_user = 'hello'
add_user(app.db, name=user_name) user = add_user(app.db, name=user_name)
add_user(app.db, name=fake_user) add_user(app.db, name=fake_user)
test_role = generate_test_role(user_name, ['read:users!group=stuff']) test_role = generate_test_role(user_name, ['read:users!group=stuff'])
roles.add_role(app.db, test_role) roles.create_role(app.db, test_role)
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test') roles.grant_role(app.db, entity=user, rolename='test')
roles.remove_obj(app.db, objname=user_name, kind='users', rolename='user') roles.strip_role(app.db, entity=user, rolename='user')
app.db.commit() app.db.commit()
r = await api_request( r = await api_request(
app, 'users', fake_user, headers=auth_header(app.db, user_name) app, 'users', fake_user, headers=auth_header(app.db, user_name)
@@ -369,9 +367,9 @@ async def test_user_filter(app):
app.db.commit() app.db.commit()
scopes = ['read:users!user=lindsay', 'read:users!user=gob', 'read:users!user=oscar'] scopes = ['read:users!user=lindsay', 'read:users!user=gob', 'read:users!user=oscar']
test_role = generate_test_role(user, scopes) test_role = generate_test_role(user, scopes)
roles.add_role(app.db, test_role) roles.create_role(app.db, test_role)
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test') roles.grant_role(app.db, entity=user, rolename='test')
roles.remove_obj(app.db, objname=user_name, kind='users', rolename='user') roles.strip_role(app.db, entity=user, 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'
@@ -402,8 +400,8 @@ async def test_service_filter(app):
user = add_user(app.db, name=user_name) user = add_user(app.db, name=user_name)
app.db.commit() app.db.commit()
test_role = generate_test_role(user, ['read:services!service=cull_idle']) test_role = generate_test_role(user, ['read:services!service=cull_idle'])
roles.add_role(app.db, test_role) roles.create_role(app.db, test_role)
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test') roles.grant_role(app.db, entity=user, rolename='test')
r = await api_request(app, 'services', headers=auth_header(app.db, user_name)) r = await api_request(app, 'services', headers=auth_header(app.db, user_name))
assert r.status_code == 200 assert r.status_code == 200
service_names = set(r.json().keys()) service_names = set(r.json().keys())
@@ -413,12 +411,12 @@ async def test_service_filter(app):
async def test_user_filter_with_group(app): async def test_user_filter_with_group(app):
# Move role setup to setup method? # Move role setup to setup method?
user_name = 'sally' user_name = 'sally'
add_user(app.db, name=user_name) user = add_user(app.db, name=user_name)
external_user_name = 'britta' external_user_name = 'britta'
add_user(app.db, name=external_user_name) add_user(app.db, name=external_user_name)
test_role = generate_test_role(user_name, ['read:users!group=sitwell']) test_role = generate_test_role(user_name, ['read:users!group=sitwell'])
roles.add_role(app.db, test_role) roles.create_role(app.db, test_role)
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test') roles.grant_role(app.db, entity=user, rolename='test')
name_set = {'sally', 'stan'} name_set = {'sally', 'stan'}
group_name = 'sitwell' group_name = 'sitwell'
@@ -441,11 +439,11 @@ async def test_user_filter_with_group(app):
async def test_group_scope_filter(app): async def test_group_scope_filter(app):
user_name = 'rollerblade' user_name = 'rollerblade'
add_user(app.db, name=user_name) user = add_user(app.db, name=user_name)
scopes = ['read:groups!group=sitwell', 'read:groups!group=bluth'] scopes = ['read:groups!group=sitwell', 'read:groups!group=bluth']
test_role = generate_test_role(user_name, scopes) test_role = generate_test_role(user_name, scopes)
roles.add_role(app.db, test_role) roles.create_role(app.db, test_role)
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test') roles.grant_role(app.db, entity=user, rolename='test')
group_set = {'sitwell', 'bluth', 'austero'} group_set = {'sitwell', 'bluth', 'austero'}
for group_name in group_set: for group_name in group_set:
@@ -462,11 +460,11 @@ async def test_group_scope_filter(app):
async def test_vertical_filter(app): async def test_vertical_filter(app):
user_name = 'lindsey' user_name = 'lindsey'
add_user(app.db, name=user_name) user = add_user(app.db, name=user_name)
test_role = generate_test_role(user_name, ['read:users:name']) test_role = generate_test_role(user_name, ['read:users:name'])
roles.add_role(app.db, test_role) roles.create_role(app.db, test_role)
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test') roles.grant_role(app.db, entity=user, rolename='test')
roles.remove_obj(app.db, objname=user_name, kind='users', rolename='user') roles.strip_role(app.db, entity=user, rolename='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))
@@ -477,12 +475,13 @@ async def test_vertical_filter(app):
async def test_stacked_vertical_filter(app): async def test_stacked_vertical_filter(app):
user_name = 'user' user_name = 'user'
user = add_user(app.db, name=user_name)
test_role = generate_test_role( test_role = generate_test_role(
user_name, ['read:users:activity', 'read:users:servers'] user_name, ['read:users:activity', 'read:users:servers']
) )
roles.add_role(app.db, test_role) roles.create_role(app.db, test_role)
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test') roles.grant_role(app.db, entity=user, rolename='test')
roles.remove_obj(app.db, objname=user_name, kind='users', rolename='user') roles.strip_role(app.db, entity=user, rolename='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))
@@ -494,13 +493,13 @@ async def test_stacked_vertical_filter(app):
async def test_cross_filter(app): async def test_cross_filter(app):
user_name = 'abed' user_name = 'abed'
add_user(app.db, name=user_name) user = add_user(app.db, name=user_name)
test_role = generate_test_role( test_role = generate_test_role(
user_name, ['read:users:activity', 'read:users!user=abed'] user_name, ['read:users:activity', 'read:users!user=abed']
) )
roles.add_role(app.db, test_role) roles.create_role(app.db, test_role)
roles.add_obj(app.db, objname=user_name, kind='users', rolename='test') roles.grant_role(app.db, entity=user, rolename='test')
roles.remove_obj(app.db, objname=user_name, kind='users', rolename='user') roles.strip_role(app.db, entity=user, rolename='user')
app.db.commit() app.db.commit()
new_users = {'britta', 'jeff', 'annie'} new_users = {'britta', 'jeff', 'annie'}
for new_user_name in new_users: for new_user_name in new_users:

View File

@@ -95,7 +95,7 @@ async def test_external_service(app):
service = app._service_map[name] service = app._service_map[name]
api_token = service.orm.api_tokens[0] api_token = service.orm.api_tokens[0]
update_roles(app.db, api_token, 'tokens', roles=['token']) update_roles(app.db, api_token, roles=['token'])
url = public_url(app, service) + '/api/users' url = public_url(app, service) + '/api/users'
r = await async_requests.get(url, allow_redirects=False) r = await async_requests.get(url, allow_redirects=False)
r.raise_for_status() r.raise_for_status()

View File

@@ -9,6 +9,7 @@ from certipy import Certipy
from jupyterhub import metrics from jupyterhub import metrics
from jupyterhub import orm from jupyterhub import orm
from jupyterhub.objects import Server from jupyterhub.objects import Server
from jupyterhub.roles import assign_default_roles
from jupyterhub.roles import update_roles from jupyterhub.roles import update_roles
from jupyterhub.utils import url_path_join as ujoin from jupyterhub.utils import url_path_join as ujoin
@@ -113,7 +114,10 @@ def add_user(db, app=None, **kwargs):
setattr(orm_user, attr, value) setattr(orm_user, attr, value)
db.commit() db.commit()
requested_roles = kwargs.get('roles') requested_roles = kwargs.get('roles')
update_roles(db, obj=orm_user, kind='users', roles=requested_roles) if requested_roles:
update_roles(db, entity=orm_user, roles=requested_roles)
else:
assign_default_roles(db, entity=orm_user)
if app: if app:
return app.users[orm_user.id] return app.users[orm_user.id]
else: else: