Skip to content

Commit

Permalink
Merge pull request jupyterhub#3661 from minrk/owner-metascope
Browse files Browse the repository at this point in the history
Rename 'all' metascope to more descriptive 'inherit'
  • Loading branch information
minrk authored Oct 27, 2021
2 parents 392dffd + 3898c72 commit dca6d37
Show file tree
Hide file tree
Showing 8 changed files with 35 additions and 29 deletions.
4 changes: 2 additions & 2 deletions docs/source/rbac/roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,13 @@ has,
define the `server` role.

To restore the JupyterHub 1.x behavior of servers being able to do anything their owners can do,
use the scope `all`:
use the scope `inherit` (for 'inheriting' the owner's permissions):

```python
c.JupyterHub.load_roles = [
{
'name': 'server',
'scopes': ['all'],
'scopes': ['inherit'],
}
]
```
Expand Down
10 changes: 6 additions & 4 deletions jupyterhub/apihandlers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,12 +308,14 @@ async def get(self):
"filter": "",
}
]
elif 'all' in raw_scopes:
raw_scopes = ['all']
elif 'inherit' in raw_scopes:
raw_scopes = ['inherit']
scope_descriptions = [
{
"scope": "all",
"description": scopes.scope_definitions['all']['description'],
"scope": "inherit",
"description": scopes.scope_definitions['inherit'][
'description'
],
"filter": "",
}
]
Expand Down
6 changes: 4 additions & 2 deletions jupyterhub/apihandlers/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,9 +397,11 @@ async def post(self, user_name):
token_roles = body.get('roles')
try:
api_token = user.new_api_token(
note=note, expires_in=body.get('expires_in', None), roles=token_roles
note=note,
expires_in=body.get('expires_in', None),
roles=token_roles,
)
except NameError:
except KeyError:
raise web.HTTPError(404, "Requested roles %r not found" % token_roles)
except ValueError:
raise web.HTTPError(
Expand Down
2 changes: 1 addition & 1 deletion jupyterhub/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2070,7 +2070,7 @@ async def init_role_assignment(self):
if role_spec['name'] == 'admin':
app_log.warning(
"Configuration specifies both admin_users and users in the admin role specification. "
"If admin role is present in config, c.authenticator.admin_users should not be used."
"If admin role is present in config, c.Authenticator.admin_users should not be used."
)
app_log.info(
"Merging admin_users set with users list in admin role"
Expand Down
18 changes: 10 additions & 8 deletions jupyterhub/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def get_default_roles():
{
'name': 'token',
'description': 'Token with same permissions as its owner',
'scopes': ['all'],
'scopes': ['inherit'],
},
]
return default_roles
Expand Down Expand Up @@ -214,7 +214,7 @@ def _check_scopes(*args, rolename=None):
or
scopes (list): list of scopes to check
Raises NameError if scope does not exist
Raises KeyError if scope does not exist
"""

allowed_scopes = set(scopes.scope_definitions.keys())
Expand All @@ -228,11 +228,13 @@ def _check_scopes(*args, rolename=None):
for scope in args:
scopename, _, filter_ = scope.partition('!')
if scopename not in allowed_scopes:
raise NameError(f"Scope '{scope}' {log_role} does not exist")
if scopename == "all":
raise KeyError("Draft scope 'all' is now called 'inherit'")
raise KeyError(f"Scope '{scope}' {log_role} does not exist")
if filter_:
full_filter = f"!{filter_}"
if not any(f in scope for f in allowed_filters):
raise NameError(
raise KeyError(
f"Scope filter '{full_filter}' in scope '{scope}' {log_role} does not exist"
)

Expand Down Expand Up @@ -322,7 +324,7 @@ def delete_role(db, rolename):
db.commit()
app_log.info('Role %s has been deleted', rolename)
else:
raise NameError('Cannot remove role %r that does not exist', rolename)
raise KeyError('Cannot remove role %r that does not exist', rolename)


def existing_only(func):
Expand Down Expand Up @@ -413,7 +415,7 @@ def _token_allowed_role(db, token, role):

expanded_scopes = _get_subscopes(role, owner=owner)

implicit_permissions = {'all', 'read:all'}
implicit_permissions = {'inherit', 'read:inherit'}
explicit_scopes = expanded_scopes - implicit_permissions
# ignore horizontal filters
no_filter_scopes = {
Expand Down Expand Up @@ -462,7 +464,7 @@ def update_roles(db, entity, roles):
"""Updates object's roles checking for requested permissions
if object is orm.APIToken
"""
standard_permissions = {'all', 'read:all'}
standard_permissions = {'inherit', 'read:inherit'}
for rolename in roles:
if isinstance(entity, orm.APIToken):
role = orm.Role.find(db, rolename)
Expand All @@ -478,7 +480,7 @@ def update_roles(db, entity, roles):
f'Requested token role {rolename} of {entity} has more permissions than the token owner'
)
else:
raise NameError('Role %r does not exist' % rolename)
raise KeyError(f'Role {rolename} does not exist')
else:
app_log.debug('Assigning default roles to %s', type(entity).__name__)
grant_role(db, entity=entity, rolename=rolename)
Expand Down
10 changes: 5 additions & 5 deletions jupyterhub/scopes.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
'description': 'Your own resources',
'doc_description': 'The user’s own resources _(metascope for users, resolves to (no_scope) for services)_',
},
'all': {
'inherit': {
'description': 'Anything you have access to',
'doc_description': 'Everything that the token-owning entity can access _(metascope for tokens)_',
},
Expand Down Expand Up @@ -317,13 +317,13 @@ def get_scopes_for(orm_object):

owner_scopes = roles.expand_roles_to_scopes(owner)

if token_scopes == {'all'}:
# token_scopes is only 'all', return owner scopes as-is
if token_scopes == {'inherit'}:
# token_scopes is only 'inherit', return scopes inherited from owner as-is
# short-circuit common case where we don't need to compute an intersection
return owner_scopes

if 'all' in token_scopes:
token_scopes.remove('all')
if 'inherit' in token_scopes:
token_scopes.remove('inherit')
token_scopes |= owner_scopes

intersection = _intersect_expanded_scopes(
Expand Down
12 changes: 6 additions & 6 deletions jupyterhub/tests/test_roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def test_orm_roles(db):
user_role = orm.Role(name='user', scopes=['self'])
db.add(user_role)
if not token_role:
token_role = orm.Role(name='token', scopes=['all'])
token_role = orm.Role(name='token', scopes=['inherit'])
db.add(token_role)
if not service_role:
service_role = orm.Role(name='service', scopes=[])
Expand Down Expand Up @@ -369,7 +369,7 @@ async def test_creating_roles(app, role, role_def, response_type, response):
'info',
app_log.info('Role user scopes attribute has been changed'),
),
('non-existing', 'test-role2', 'error', NameError),
('non-existing', 'test-role2', 'error', KeyError),
('default', 'user', 'error', ValueError),
],
)
Expand Down Expand Up @@ -410,9 +410,9 @@ async def test_delete_roles(db, role_type, rolename, response_type, response):
},
'existing',
),
({'name': 'test-scopes-2', 'scopes': ['uses']}, NameError),
({'name': 'test-scopes-3', 'scopes': ['users:activities']}, NameError),
({'name': 'test-scopes-4', 'scopes': ['groups!goup=class-A']}, NameError),
({'name': 'test-scopes-2', 'scopes': ['uses']}, KeyError),
({'name': 'test-scopes-3', 'scopes': ['users:activities']}, KeyError),
({'name': 'test-scopes-4', 'scopes': ['groups!goup=class-A']}, KeyError),
],
)
async def test_scope_existence(tmpdir, request, role, response):
Expand All @@ -431,7 +431,7 @@ async def test_scope_existence(tmpdir, request, role, response):
assert added_role is not None
assert added_role.scopes == role['scopes']

elif response == NameError:
elif response == KeyError:
with pytest.raises(response):
roles.create_role(db, role)
added_role = orm.Role.find(db, role['name'])
Expand Down
2 changes: 1 addition & 1 deletion jupyterhub/tests/test_scopes.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ async def test_metascope_all_expansion(app, create_user_with_scopes):
user = create_user_with_scopes('self')
user.new_api_token()
token = user.api_tokens[0]
# Check 'all' expansion
# Check 'inherit' expansion
token_scope_set = get_scopes_for(token)
user_scope_set = get_scopes_for(user)
assert user_scope_set == token_scope_set
Expand Down

0 comments on commit dca6d37

Please sign in to comment.