diff --git a/docs/source/_static/rest-api.yml b/docs/source/_static/rest-api.yml index d02857dd..b636d0ae 100644 --- a/docs/source/_static/rest-api.yml +++ b/docs/source/_static/rest-api.yml @@ -1327,15 +1327,15 @@ components: roles: type: array description: - Deprecated in JupyterHub 2.3, always an empty list. Tokens - have 'scopes' starting from JupyterHub 2.3. + Deprecated in JupyterHub 3, always an empty list. Tokens have + 'scopes' starting from JupyterHub 3. items: type: string scopes: type: array description: List of scopes this token has been assigned. New in JupyterHub - 2.3. In JupyterHub 2.0-2.2, tokens were assigned 'roles' insead of scopes. + 3. In JupyterHub 2.x, tokens were assigned 'roles' insead of scopes. items: type: string note: diff --git a/docs/source/changelog.md b/docs/source/changelog.md index d8ad9704..2b1c146b 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -18,13 +18,21 @@ It qualifies as a major upgrade because of two changes: The schema change should not be disruptive, but we've decided that any schema change qualifies as a major version upgrade. 2. We've dropped support for Python 3.6, which reached End-of-Life in 2021. - If you are using at least Python 3.7, this should have no effect. + If you are using at least Python 3.7, this change should have no effect. + +The database schema change is small and should not be disruptive, +but downgrading is always harder than upgrading after a db migration, +which makes rolling back the update more likely to be problematic. #### Changes in RBAC -The biggest changes in 3.0 relate to RBAC: -After 2.0, we learned that we used roles in a few places -that should have been scopes. +The biggest changes in 3.0 relate to {ref}`RBAC`, +which also means they shouldn't affect most users. +The users most affected will be JupyterHub admins using JupyterHub roles +extensively to define user permissions. + +After testing 2.0 in the wild, +we learned that we had used _roles_ in a few places that should have been _scopes_. Specifically, OAuth tokens now have _scopes_ instead of _roles_ (and token-issuing oauth clients now have `allowed_scopes` instead of `allowed_roles`). The consequences should be fairly transparent to users, @@ -34,27 +42,31 @@ We tried not to break anything here, so any prior use of roles will still work w but the role will be resolved _immediately_ at token-issue time, rather than every time the token is used. -This especially came up testing the new [custom scopes] feature. +This especially came up testing the new {ref}`custom-scopes` feature. Authors of JupyterHub-authenticated services can now extend JupyterHub's RBAC functionality to define their own scopes, and assign them to users and groups via roles. This can be used to e.g. limit student/grader/instructor permissions in a grading service, or grant instructors read-only access to their students' single-user servers starting with upcoming Jupyter Server 2.0. Further extending granular control of permissions, -we have added `!service` and `!server` filters for scopes, +we have added `!service` and `!server` filters for scopes (:ref:`self-referencing-filters`), like we had for `!user`. -These make it easier for single-user servers or services -to have permission (or issue tokens with permission) to take actions -only about themselves (e.g. access to auth state, start/stop, or custom scopes). Access to the admin UI is now governed by a dedicated `admin-ui` scope, rather than combined `admin:servers` and `admin:users` in 2.0. -This means that actions to take _via_ the UI, and access _to_ the UI are separated. -For example, it generally doesn't make sense to grant `admin-ui` without at least `list:users` for some subset of users. +More info in `ref`{available-scopes-target}. #### More highlights -- TODO +- The admin UI can now show more detailed info about users and their servers in a drop-down details table: + + ![Details view in admin UI](./images/dropdown-details-3.0.png) + +- Several bugfixes and improvements in the new admin UI. +- Direct access to the Hub's database is deprecated. + We intend to change the database connection lifecycle in the future to enable scalability and high-availability (HA), + and limiting where connections and transactions can occur is an important part of making that possible. +- Lots more bugfixes and error-handling improvements. ([full changelog](https://github.com/jupyterhub/jupyterhub/compare/2.3.1...ab776e3989bffe9e1a9d0744c96c5f8e8d876988)) diff --git a/docs/source/conf.py b/docs/source/conf.py index d69b3cee..63d61624 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -48,7 +48,7 @@ version = '%i.%i' % jupyterhub.version_info[:2] # The full version, including alpha/beta/rc tags. release = jupyterhub.__version__ -language = None +language = "en" exclude_patterns = [] pygments_style = 'sphinx' todo_include_todos = False diff --git a/docs/source/images/dropdown-details-3.0.png b/docs/source/images/dropdown-details-3.0.png new file mode 100644 index 00000000..522828f9 Binary files /dev/null and b/docs/source/images/dropdown-details-3.0.png differ diff --git a/docs/source/rbac/index.md b/docs/source/rbac/index.md index 6fb102f9..3be61c54 100644 --- a/docs/source/rbac/index.md +++ b/docs/source/rbac/index.md @@ -1,3 +1,5 @@ +(RBAC)= + # JupyterHub RBAC Role Based Access Control (RBAC) in JupyterHub serves to provide fine grained control of access to Jupyterhub's API resources. diff --git a/docs/source/rbac/roles.md b/docs/source/rbac/roles.md index c8e4edb5..551e0cf9 100644 --- a/docs/source/rbac/roles.md +++ b/docs/source/rbac/roles.md @@ -27,7 +27,6 @@ Roles can be assigned to the following entities: - Users - Services - Groups -- Tokens An entity can have zero, one, or multiple roles, and there are no restrictions on which roles can be assigned to which entity. Roles can be added to or removed from entities at any time. @@ -41,7 +40,7 @@ Services do not have a default role. Services without roles have no access to th A group does not require any role, and has no roles by default. If a user is a member of a group, they automatically inherit any of the group's permissions (see {ref}`resolving-roles-scopes-target` for more details). This is useful for assigning a set of common permissions to several users. **Tokens** \ -A token’s permissions are evaluated based on their owning entity. Since a token is always issued for a user or service, it can never have more permissions than its owner. If no specific scopes are requested for a new token, the token is assigned the `token` role. +A token’s permissions are evaluated based on their owning entity. Since a token is always issued for a user or service, it can never have more permissions than its owner. If no specific scopes are requested for a new token, the token is assigned the scopes of the `token` role. (define-role-target)= diff --git a/docs/source/rbac/scopes.md b/docs/source/rbac/scopes.md index b06d4d35..d2046dc1 100644 --- a/docs/source/rbac/scopes.md +++ b/docs/source/rbac/scopes.md @@ -72,6 +72,8 @@ Requested resources are filtered based on the filter of the corresponding scope. In case a user resource is being accessed, any scopes with _group_ filters will be expanded to filters for each _user_ in those groups. +(self-referencing-filters)= + ### Self-referencing filters There are some 'shortcut' filters, @@ -82,7 +84,7 @@ The `!user` filter is a special horizontal filter that strictly refers to the ** For example, the `server` role assigned by default to server tokens contains `access:servers!user` and `users:activity!user` scopes. This allows the token to access and post activity of only the servers owned by the token owner. -:::{versionadded} 2.3 +:::{versionadded} 3.0 `!service` and `!server` filters. ::: @@ -130,6 +132,45 @@ There are four exceptions to the general {ref}`scope conventions ` can be added to scopes to customize them. \ Metascopes `self` and `all`, ``, `:`, `read:`, `admin:`, and `access:` scopes are predefined and cannot be changed otherwise. @@ -139,10 +180,10 @@ Metascopes `self` and `all`, ``, `:`, `read:` and {ref}`horizontal filtering `, limiting or elevated permissions (`read:` or `admin:`, respectively), and metascopes. -Roles and scopes are resolved on several occasions, for example when requesting an API token with specific roles or making an API request. The following sections provide more details. +Roles and scopes are resolved on several occasions, for example when requesting an API token with specific scopes or making an API request. The following sections provide more details. (requesting-api-token-target)= -### Requesting API token with specific roles +### Requesting API token with specific scopes -:::{versionchanged} 2.3 +:::{versionchanged} 3.0 API tokens have _scopes_ instead of roles, so that their permissions cannot be updated. You may still request roles for a token, but those roles will be evaluated to the corresponding _scopes_ immediately. + +Prior to 3.0, tokens stored _roles_, +which meant their scopes were resolved on each request. ::: API tokens grant access to JupyterHub's APIs. The RBAC framework allows for requesting tokens with specific permissions. -As of JupyterHub 2.3, it is only possible to specify scopes for a token through the _POST /users/:name/tokens_ API where the scopes can be specified in the token parameters body (see [](../reference/rest-api.rst)). -RBAC adds several steps into the token issue flow. +RBAC is involved in several stages of the OAuth token flow. -If no scopes are requested, the token is issued with the permissions stored on the default `token` role +When requesting a token via the tokens API (`/users/:name/tokens`), or the token page (`/hub/token`), +if no scopes are requested, the token is issued with the permissions stored on the default `token` role (providing the requester is allowed to create the token). +OAuth tokens are also requested via OAuth flow + If the token is requested with any scopes, the permissions of requesting entity are checked against the requested permissions to ensure the token would not grant its owner additional privileges. -If, due to modifications of roles or entities, at API request time a token has any scopes that its owner does not, those scopes are removed. -The API request is resolved without additional errors using the scope _intersection_, -but the Hub logs a warning (see {ref}`Figure 2 `). +If, due to modifications of permissions of the token or token owner, +at API request time a token has any scopes that its owner does not, +those scopes are removed. +The API request is resolved without additional errors using the scope _intersection_; +the Hub logs a warning in this case (see {ref}`Figure 2 `). -Resolving a token's scope (yellow box in {ref}`Figure 1 `) corresponds to resolving all the token's owner roles (including the roles associated with their groups) and the token's requested roles into a set of scopes. The two sets are compared (Resolve the scopes box in orange in {ref}`Figure 1 `), taking into account the scope hierarchy but, solely for role assignment, omitting any {ref}`horizontal filter ` comparison. If the token's scopes are a subset of the token owner's scopes, the token is issued with the requested roles; if not, JupyterHub will raise an error. +Resolving a token's scope (yellow box in {ref}`Figure 1 `) corresponds to resolving all the token's owner roles (including the roles associated with their groups) and the token's own scopes into a set of scopes. The two sets are compared (Resolve the scopes box in orange in {ref}`Figure 1 `), taking into account the scope hierarchy. +If the token's scopes are a subset of the token owner's scopes, the token is issued with the requested scopes; if not, JupyterHub will raise an error. {ref}`Figure 1 ` below illustrates the steps involved. The orange rectangles highlight where in the process the roles and scopes are resolved. @@ -67,9 +75,9 @@ Figure 1. Resolving roles and scopes during API token request ### Making an API request -With the RBAC framework each authenticated JupyterHub API request is guarded by a scope decorator that specifies which scopes are required to gain the access to the API. +With the RBAC framework, each authenticated JupyterHub API request is guarded by a scope decorator that specifies which scopes are required to gain the access to the API. -When an API request is performed, the requesting API token's roles are again resolved (yellow box in {ref}`Figure 2 `) to ensure the token does not grant more permissions than its owner has at the request time (e.g., due to changing/losing roles). +When an API request is performed, the requesting API token's scopes are again intersected with its owner's (yellow box in {ref}`Figure 2 `) to ensure the token does not grant more permissions than its owner has at the request time (e.g., due to changing/losing roles). If the owner's roles do not include some scopes of the token's scopes, only the _intersection_ of the token's and owner's scopes will be used. For example, using a token with scope `users` whose owner's role scope is `read:users:name` will result in only the `read:users:name` scope being passed on. In the case of no _intersection_, an empty set of scopes will be used. The passed scopes are compared to the scopes required to access the API as follows: diff --git a/jupyterhub/orm.py b/jupyterhub/orm.py index c79a7eab..ea8f54d8 100644 --- a/jupyterhub/orm.py +++ b/jupyterhub/orm.py @@ -714,7 +714,7 @@ class APIToken(Hashed, Base): # evaluate roles to scopes immediately # TODO: should this be deprecated, or not? # warnings.warn( - # "Setting roles on tokens is deprecated in JupyterHub 2.2. Use scopes.", + # "Setting roles on tokens is deprecated in JupyterHub 3.0. Use scopes.", # DeprecationWarning, # stacklevel=3, # )