From a2b76bceb9de40cad9cdd4704591ca6adee1f65a Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 22 Apr 2021 13:39:36 +0200 Subject: [PATCH] minor copy-editing, TODOs in rbac docs --- docs/source/rbac/index.md | 9 +++++++-- docs/source/rbac/roles.md | 25 +++++++++++++++++-------- docs/source/rbac/scopes.md | 4 ++-- docs/source/rbac/tech-implementation.md | 22 ++++++++++++---------- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/docs/source/rbac/index.md b/docs/source/rbac/index.md index 5963af63..3f9ed98f 100644 --- a/docs/source/rbac/index.md +++ b/docs/source/rbac/index.md @@ -2,11 +2,16 @@ Role Based Access Control (RBAC) in JupyterHub serves to provide fine grained control of access to Jupyterhub's API resources. +RBAC is new in JupyterHub 2.0. + ## Motivation -The JupyterHub API requires authorization before allowing changes to the backend. For instance, currently the default behaviour is that creating or deleting users requires _admin rights_. This ensures that an arbitrary user, or even an unauthenticated third party, are not allowed to perform such actions. +The JupyterHub API requires authorization to access its APIs. +This ensures that an arbitrary user, or even an unauthenticated third party, are not allowed to perform such actions. +For instance, the behaviour prior to adoption of RBAC is that creating or deleting users requires _admin rights_. -This system is functional, but lacks flexibility. If your Hub serves a number of users in different groups, you might want to delegate permissions to other users or automate certain processes. With the current framework, appointing a 'group-only admin' or a bot that culls idle servers, requires granting full rights to all actions. This poses a risk of the user or service intentionally or unintentionally accessing and modifying any data within the Hub and violates the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege). +The prior system is functional, but lacks flexibility. If your Hub serves a number of users in different groups, you might want to delegate permissions to other users or automate certain processes. +Prior to RBAC, appointing a 'group-only admin' or a bot that culls idle servers, requires granting full admin rights to all actions. This poses a risk of the user or service intentionally or unintentionally accessing and modifying any data within the Hub and violates the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege). To remedy situations like this, JupyterHub is transitioning to an RBAC system. By equipping users, groups and services with _roles_ that supply them with a collection of permissions (_scopes_), administrators are able to fine-tune which parties are granted access to which resources. diff --git a/docs/source/rbac/roles.md b/docs/source/rbac/roles.md index 60df2062..49aeaeda 100644 --- a/docs/source/rbac/roles.md +++ b/docs/source/rbac/roles.md @@ -3,7 +3,7 @@ JupyterHub provides four roles that are available by default: ```{admonition} **Default roles** -- `user` role provides a {ref}`default user scope ` `self` that grants access to only the user's own resources. +- `user` role provides a {ref}`default user scope ` `self` that grants access to the user's own resources. - `admin` role contains all available scopes and grants full rights to all actions similarly to the current admin status. This role **cannot be edited**. - `token` role provides a {ref}`default token scope ` `all` that resolves to the same permissions as the token's owner has. - `server` role allows for posting activity of "itself" only. The scope is currently under development. @@ -11,7 +11,12 @@ JupyterHub provides four roles that are available by default: **These roles cannot be deleted.** ``` -New roles can also be customly defined (see {ref}`define_role_target`). Roles can be assigned to the following entities: +The `user`, `admin`, and `token` roles by default all preserve the permissions prior to RBAC. +Only the `server` role is changed from pre-2.0, to reduce its permissions to activity-only +instead of the default of a full access token. + +Additional custom roles can also be defined (see {ref}`define_role_target`). +Roles can be assigned to the following entities: - Users - Services @@ -24,7 +29,7 @@ An entity can have zero, one, or multiple roles, and there are no restrictions o When a new user or service gets created, they are assigned their default role ( `user` or `admin`) if no custom role is requested, currently based on their admin status. **Groups** \ -A group does not require any role, and has no roles by default. If a user is a member of a group, they autimatically 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. +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 role is requested for a new token, the token is assigned the `token` role. @@ -35,6 +40,11 @@ A token’s permissions are evaluated based on their owning entity. Since a toke Roles can be defined or modified in the configuration file as a list of dictionaries. An example: +% TODO: think about loading users/tokens into roles if membership has been changed via API. +% What should be the result? +% What happens if a user is _removed_ from this list? +% Do they lose their role assignment or keep it? + ```python # in jupyterhub_config.py @@ -89,14 +99,13 @@ In a role definition, the `name` field is required, while all other fields are o - start with a letter - end with letter or number. -If no scopes are defined for new role, JupyterHub will raise a warning. Providing non-existing scopes will result in an error.\ -Moreover, `users`, `services`, `groups` and `tokens` only accept objects that already exist or are defined previously in the file.\ +If no scopes are defined for new role, JupyterHub will raise a warning. Providing non-existing scopes will result in an error. +Moreover, `users`, `services`, `groups`, and `tokens` only accept objects that already exist or are defined previously in the file. It is not possible to implicitly add a new user to the database by defining a new role. ``` -\ -In case the role with a certain name already exists in the database, its definition and scopes will be overwritten. This holds true for all roles except the `admin` role, which cannot be overwritten; an error will be raised if trying to do so. \ +In case the role with a certain name already exists in the database, its definition and scopes will be overwritten. This holds true for all roles except the `admin` role, which cannot be overwritten; an error will be raised if trying to do so. Any previously defined role bearers for this role will remain the role bearers but their permissions will change if the role's permissions are overwritten. The newly defined bearers (in this case `maria` and `joe` and `external`) will be added to the existing ones. -Once a role is loaded, it remains in the database until explicitly deleting it through `delete_role()` function in `roles.py`. Default roles cannot be deleted. \ +Once a role is loaded, it remains in the database until explicitly deleting it through `delete_role()` function in `roles.py`. Default roles cannot be deleted. Omitting the `c.JupyterHub.load_roles` or specifying different roles in the `jupyterhub_config.py` file on the next startup will not erase previously defined roles, nor their bearers. diff --git a/docs/source/rbac/scopes.md b/docs/source/rbac/scopes.md index a368776d..9478525d 100644 --- a/docs/source/rbac/scopes.md +++ b/docs/source/rbac/scopes.md @@ -4,7 +4,7 @@ A scope has a syntax-based design that reveals which resources it provides acces `` in the RBAC scope design refers to the resource name in the [JupyterHub's API](../reference/rest-api.rst) endpoints in most cases. For instance, `` equal to `users` corresponds to JupyterHub's API endpoints beginning with _/users_. -## Scope syntax +## Scope conventions - `` \ The `` scopes, such as `users` or `groups`, grant read and write permissions to the resource itself and all its sub-resources. E.g., the scope `users:servers` is included within the scope `users`. @@ -15,7 +15,7 @@ A scope has a syntax-based design that reveals which resources it provides acces +++ - `admin:` \ - Grants create/delete permissions on the corresponding resource in addition to read and write permissions. + Grants additional permissions such as create/delete on the corresponding resource in addition to read and write permissions. +++ - `:` \ diff --git a/docs/source/rbac/tech-implementation.md b/docs/source/rbac/tech-implementation.md index c1e353e8..7df666a2 100644 --- a/docs/source/rbac/tech-implementation.md +++ b/docs/source/rbac/tech-implementation.md @@ -1,12 +1,14 @@ # Technical Implementation -Roles are stored in the database similarly as users, services, etc., and can be added or modified as explained in {ref}`define_role_target` section. Users, services, groups and tokens can gain, change and lose roles. For example, one can change a token's role, and as such its permissions, without the need to initiate new token (currently through `update_roles()` and `strip_role()` functions in `roles.py`, this will be eventually available through APIs). Roles' and scopes' utilities can be found in `roles.py` and `scopes.py` modules. +Roles are stored in the database, where they are associated with users, services, etc., and can be added or modified as explained in {ref}`define_role_target` section. Users, services, groups, and tokens can gain, change, and lose roles. +For example, one can change a token's role, and thereby its permissions, without the need to issue a new token (currently through `update_roles()` and `strip_role()` functions in `roles.py`, this will be eventually available through APIs). +Roles' and scopes' utilities can be found in `roles.py` and `scopes.py` modules. (resolving-roles-scopes-target)= ## Resolving roles and scopes -**Resolving roles** refers to determining which roles a user, service, token or group has, extracting the list of scopes from each role and combining them into a single set of scopes. +**Resolving roles** refers to determining which roles a user, service, token, or group has, extracting the list of scopes from each role and combining them into a single set of scopes. **Resolving scopes** involves expanding scopes into all their possible subscopes and, if applicable, comparing two sets of scopes. Both procedures take into account the scope hierarchy, {ref}`vertical ` and {ref}`horizontal filtering ` limiting or elevated permissions (`read:` or `admin:`, respectively), and metascopes. @@ -19,15 +21,15 @@ API tokens grant access to JupyterHub's APIs. The RBAC framework allows for requ 1. through the `jupyterhub_config.py` file as described in the {ref}`define_role_target` section 2. through the _POST /users/:name/tokens_ API where the roles can be specified in the token parameters body (see [](../reference/rest-api.rst)). -The RBAC framework adds several steps into the token issue flow. +RBAC adds several steps into the token issue flow. -If no roles are requested, the token is issued with a default role (providing the requester is allowed to create the token). +If no roles are requested, the token is issued with the default `token` role (providing the requester is allowed to create the token). -If the token is requested with any roles, the permissions of requesting entity are checked against the requested permissions to ensure the token will not grant its owner additional privileges. +If the token is requested with any roles, 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 not taken into account. The API request is resolved without additional errors, but the Hub logs a warning (see {ref}`Figure 2 `). +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, but the Hub logs a warning (see {ref}`Figure 2 `). -Resolving token's roles (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 roles (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. {ref}`Figure 1 ` below illustrates the steps involved. The orange rectangles highlight where in the process the roles and scopes are resolved. @@ -42,12 +44,12 @@ Figure 1. Resolving roles and scopes during API token request The above check is also performed when roles are requested for existing tokens, e.g., when adding tokens to {ref}`role definitions ` through the `jupyterhub_config.py`. ``` -### Making API 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. -When an API request is performed, the passed 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). -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 passed further. For example, using a token with scope `users` whose owner's role scope is `read:users:name` will results in only the `read:users:name` scope being passed on. In the case of no intersection, en empty set of scopes will be passed on. +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). +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 results 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: