New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
allow user-defined custom scopes #3713
Conversation
Can be combined with jupyter-server/jupyter_server#165 to extend granular permissions (e.g. read-only support) to single-user servers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work on this @minrk!
I wonder about subscopes defining subscopes. I'm thinking perhaps we should that we validate they don't if they aren't allowed to do so, or we should test for it if they are allowed to do so.
They definitely are. I'll make sure that:
|
Working on an example for this has revealed something of an issue. The following aspects of current behavior are causing a problem:
Because in my test example, I want some users to have write access to the service and others to have read:
and I currently have two choices:
So far, I think the only solution is to allow for services to request more permissions than they will receive. That means either:
I feel more secure in implementing the first, but as I sketch example config, it seems extremely tedious to have to write out a role for every possible combination of user permissions. Plus, if roles change, we are forced to rely on the runtime scope intersectin anyway. The second should work today just by removing the check preventing issuing tokens with more permissions than their owners, because we already reduce token scopes to subsets of owner scopes at request time. The main thing it changes is moving the state of a token having more permissions than its owner to a 'normal' situation, rather than an exceptional one. |
Your rationale for option two makes sense, and in some ways it's similar to the permissions model for e.g. phone apps. An app can request a wide range of permissions but a user can refuse individual permissions, and it's up to the app to handle that gracefully. This means it's important to document this behaviour for developers of services. |
@manics yup, that's the conclusion we came to after a chat with @consideRatio. Working on the implementation now, and will write up some notes and make sure the docs are clear. The gist of the updates I want to do here:
The mitigation of the step I didn't like (that if the user's permissions are escalated, the token's permissions may be, too) that I plan to explore is revoking tokens whose scopes have expanded. i.e. during startup: for each user whose permissions increased:
for each token:
if token permissions increased as a result:
revoke This require's the ability to easily evaluate before/after scopes, which I'm not sure is that doable. Ultimately, one conclusion I came to was that role assignments for tokens was perhaps not the right choice - tokens should probably have been assigned static lists of scopes directly, and reserve roles for users/groups/services. That's a tough change to make at this point, though. Several things would be easier if we did fully resolve final permissions when tokens are issued, rather than relying on roles. |
After looking into some implementations, I'm increasingly convinced that tokens should resolve to fixed scopes immediately upon issue, rather than have lazily-evaluated permissions (roles) on tokens themselves, which can change over time. That makes things hairier. My plan:
|
To avoid this PR getting too big, I think I want to do it in 3 PRs, for easier review:
|
defined with c.JupyterHub.custom_scopes = { 'custom:scope': {'description': "text shown on oauth confirm"} } Allows injecting custom scopes to roles, allowing extension of granular permissions to service-defined custom scopes. Custom scopes: - MUST start with `custom:` - MUST only contain ascii lowercase, numbers, colon, hyphen, asterisk, underscore - MUST define a `description` - MAY also define `subscopes` list(s), each of which must also be explicitly defined HubAuth can be used to retrieve and check for custom scopes to authorize requests.
I believe this is ready to go. There are two changes here:
This is enough to make a 'grader' service useful, which I've included in the examples. It isn't as far as we want to go for e.g. read-only access to just one single-user server (that needs subsetting at the token level, and specific scopes on tokens), but I think it's enough for this PR to be complete, and I'll move on to separate PRs for the two later steps. |
- oauth clients can request a list of roles - authorization will proceed with the _subset_ of those roles held by the user - in the future, this subsetting will be refined to the scope level
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@minrk amazing work, especially on tests and examples!
I've only added comments about documentation and such, this otherwise LGTM!
@consideRatio thanks for the feedback! I think it's addressed now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, was about to merge just as tests passed, but another push arrived :p
defined with
Allows injecting custom scopes to roles,
allowing extension of granular permissions to service-defined custom scopes (e.g. jupyter-server/jupyter_server#165).
Custom scopes:
custom:
description
subscopes
list(s), each of which must also be explicitly definedBecause scopes are always retrieved from the oauth
/api/user
endpoint, HubAuth can be used to retrieve and check for custom scopes to authorize requests.TODO: