From 03d189771f75d44c7c4901e38a5d46fd1f2fe8aa Mon Sep 17 00:00:00 2001 From: vinicius douglas cerutti Date: Fri, 24 Jan 2025 13:17:58 -0300 Subject: [PATCH 01/11] include fine-grained section and add missing pieces --- docs/docs/how-tos/fine-grained-permissions.md | 266 +++++++++++++----- 1 file changed, 200 insertions(+), 66 deletions(-) diff --git a/docs/docs/how-tos/fine-grained-permissions.md b/docs/docs/how-tos/fine-grained-permissions.md index b95c4813c..18e323288 100644 --- a/docs/docs/how-tos/fine-grained-permissions.md +++ b/docs/docs/how-tos/fine-grained-permissions.md @@ -4,9 +4,15 @@ title: Creating and Managing Groups, Roles, and Directories description: How to configure Keycloak's permissions, groups and roles, and manage group directories in JupyterHub. --- -Groups are a fundamental and vital part of the Nebari ecosystem. They are used to manage access to a wide range of services within Nebari, including JupyterHub instances, Keycloak realms, Conda environments, and computing resources. By grouping users based on roles, projects, or departments, Nebari simplifies the management of permissions and resource sharing. +Groups are a fundamental and vital part of the Nebari ecosystem. They are used to manage access to a wide range of +services within Nebari, including JupyterHub instances, Keycloak realms, Conda environments, and computing resources. By +grouping users based on roles, projects, or departments, Nebari simplifies the management of permissions and resource +sharing. -Beyond managing access, groups play a crucial role in organizing and sharing data across the JupyterHub ecosystem. Each group can have its own shared directory within JupyterHub, allowing users within the same group to collaborate seamlessly by sharing files and resources. This facilitates team collaboration and ensures that data is organized and accessible to those who need it. +Beyond managing access, groups play a crucial role in organizing and sharing data across the JupyterHub ecosystem. Each +group can have its own shared directory within JupyterHub, allowing users within the same group to collaborate +seamlessly by sharing files and resources. This facilitates team collaboration and ensures that data is organized and +accessible to those who need it. In this document, we will cover: @@ -17,9 +23,12 @@ In this document, we will cover: ## Managing Groups in Keycloak -Keycloak is the identity and access management service used in Nebari. It allows you to create and manage users, groups, roles, and permissions. Groups in Keycloak are collections of users, and they can be assigned specific roles that grant permissions to access various services within Nebari. +Keycloak is the identity and access management service used in Nebari. It allows you to create and manage users, groups, +roles, and permissions. Groups in Keycloak are collections of users, and they can be assigned specific roles that grant +permissions to access various services within Nebari. -For detailed information on managing groups in Keycloak, refer to the [Keycloak documentation on Group Management](https://www.keycloak.org/docs/latest/server_admin/#_group_management). +For detailed information on managing groups in Keycloak, refer to the +[Keycloak documentation on Group Management](https://www.keycloak.org/docs/latest/server_admin/#_group_management). Below we outline the steps specific to Nebari. @@ -29,26 +38,40 @@ To create a new group in Keycloak: 1. **Log in to Keycloak** as an administrator (usually the `root` user). 2. **Navigate to Groups**: Click on **Groups** in the left-hand menu. -3. **Create the Group**: Click the **New** button, enter an appropriate name for your new group (e.g., `conda-store-manager`), and save. +3. **Create the Group**: Click the **New** button, enter an appropriate name for your new group (e.g., + `conda-store-manager`), and save. -If you wish to organize your groups hierarchically, you can create subgroups by selecting a parent group and adding a subgroup. +If you wish to organize your groups hierarchically, you can create subgroups by selecting a parent group and adding a +subgroup. # Fine Grained Permissions via Keycloak -Nebari provides its users (particularly admins) a way to manage roles and permissions to -various services like `jupyterhub` and `conda-store` via Keycloak. The idea is to be able to manage -roles and permissions from a central place, in this case Keycloak. An admin or anyone who has -permissions to create a role in Keycloak will create role(s) with assigned scopes (permissions) -to it and attach it to user(s) or group(s). +Nebari uses Keycloak to centrally manage roles, permissions, and scopes for various services like `jupyterhub` and +`conda-store`. By default, Nebari provides a set of predefined clients and default roles that cover common needs—such as +basic view/read permissions or full admin control, a full list of which can be found in the [] section. -These roles are created and attached from keycloak's interface and scoped for a particular -client (i.e. a Nebari service such as `jupyterhub` or `conda-store`). This means the roles for a -particular service (say `jupyterhub`) should be created within the Keycloak client named -`jupyterhub`. +However, certain environments require more specialized or granular access controls. In these cases, administrators (or +any user with appropriate privileges) can create custom roles and scopes directly in Keycloak. This allows you to tailor +the permission model to fit your precise security and governance requirements—all while still benefiting from the +ready-to-use defaults Nebari provides. -By default, Nebari comes with several custom clients included in a fresh deployment. -These clients facilitate various services and integrations within the Nebari ecosystem. -The predefined clients are as follows: +## Managing Roles and Permissions +Overview +- *Roles*: Define a set of permissions (or scopes) for a particular client (service). +- *Groups*: Aggregate users who share similar permissions. +- *Scopes*: The individual permissions (e.g., “read:users:name” for reading user names) that can be attached to a role. + +By creating custom roles with specific scopes, and assigning them to groups or users, you can fine-tune exactly who can +do what in your Nebari environment. More details on how to create roles and scopes can be found in the +[Creating a Role](#creating-a-role) section. + +## Default Clients and permissions in Nebari + +Nebari comes with several custom Keycloak clients in a fresh deployment. These map to key Nebari services and +integrations: + +By default, Nebari comes with several custom clients included in a fresh deployment. These clients facilitate various +services and integrations within the Nebari ecosystem. The predefined clients are as follows: ```yaml clients: @@ -59,35 +82,105 @@ clients: - forwardauth ``` -To manage and configure these clients, you can navigate to the `Clients` tab within the -Keycloak admin console, as illustrated in the image below. +To manage and configure these clients, you can navigate to the `Clients` tab within the Keycloak admin console, as +illustrated in the image below. ![Keycloak clients](/img/how-tos/fine_grainer_permissions_keycloak_clients.png) This can be accessed at `/auth/admin/master/console/#/realms/nebari/clients` -## Creating a Role +Groups represent a collection of users that perform similar actions and therefore require similar permissions. By +default, Nebari is deployed with the following groups: `admin`, `developer`, and `analyst` (in roughly descending order +of permissions and scope). + +:::info +Users in a particular group will also get access to that groups shared folder. So if `user A` belongs to the +`developer`, they will also have access to the `~/shared/developer` folder. This also applies to new groups that you +create. +::: + +Roles on the other hand represent the type or category of user. This includes access and permissions that this category +of user will need to perform their regular job duties. The differences between `groups` and `roles` are subtle. +Particular roles (one or many), like `conda_store_admin`, are associated with a particular group, such as `admin` and +any user in this group will then assume the role of `conda_store_admin`. + +:::info +These roles can be stacked. This means that if a user is in one group with role `conda_store_admin` and another group +with role `conda_store_viewer`, this user ultimately has the role `conda_store_admin`. +::: + +## In-depth look at Roles and Groups -The process for creating a role is similar, irrespective of the service. To create a role for a -service +Below is a table illustrating each default group, the resources they can access, and the roles they inherit. The table +also includes a brief description of the permissions granted to each group. + +| Group | Access to Nebari Resources | Roles | Permissions Description | +| ------------ | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `analyst` | | | | +| `developer` | | | | +| `admin` | | | | +| `superadmin` | | | | + +:::info +Check +[Conda-store authorization model](https://conda-store.readthedocs.io/en/latest/contributing.html#authorization-model) +for more details on conda-store authorization. +::: + +:::caution +The role `jupyterhub_admin` gives users elevated permissions to JupyterHub and should be applied judiciously. As +mentioned in the table above, a JupyterHub admin is able to impersonate other users and view the contents of their home +folder. For more details, read through the +[JupyterHub documentation](https://z2jh.jupyter.org/en/stable/jupyterhub/customizing/user-management.html#admin-users). +::: + +To create new groups or modify (or delete) existing groups, log in as `root` and click **Groups** on the left-hand side. + +As an example, we create a new group named `conda-store-manager`. This group will have administrator access to the +[Conda-Store service]. + +1. Click **New** in the upper-right hand corner under **Groups**. + +![Keycloak groups tab screenshot - user groups view](/img/how-tos/keycloak_groups.png) + +- Then, give the new group an appropriate name. + +![Keycloak add group form - name field set to conda-store-manager](/img/how-tos/keycloak_new_group1.png) + +2. Under **Role Mapping**, add the appropriate **Client Roles** as needed; there should be no need to update the **Realm + Roles**. + +![Keycloak group conda-store-manager form - role mappings tab focused with expanded client roles dropdown](/img/how-tos/keycloak_new_group2.png) + +In this example, the new group only has one mapped role, `conda_store_admin`; however, it's possible to attach multiple +**Client Roles** to a single group. + +![Keycloak group conda-store-manager form - role mappings tab focused ](/img/how-tos/keycloak_new_group3.png) + +Once complete, return to the **Users** section in the dashboard and add the relevant users to this newly created group. + +### Creating a Role + +The process for creating a role is similar, irrespective of the service. To create a role for a service 1. Select the appropriate client and click on "Add Role". ![Keycloak client add jupyterhub role](/img/how-tos/keycloak_jupyterhub_client.png) -2. On the "Add Role" form, write a meaningful name and description for the role. Be sure to include what this role intends to accomplish. Click "Save". +2. On the "Add Role" form, write a meaningful name and description for the role. Be sure to include what this role + intends to accomplish. Click "Save". ![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_jupyterhub_add_role.png) -3. Now the role has been created, but it does nothing. Let's add some permissions to it by clicking on the "Attributes" tab - and adding scopes. The following sections will explain the `components` and `scopes` in more detail. +3. Now the role has been created, but it does nothing. Let's add some permissions to it by clicking on the "Attributes" + tab and adding scopes. The following sections will explain the `components` and `scopes` in more detail. ![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_add_role_attributes.png) -## Adding Role to Group(s) / User(s) +### Adding Role to Group(s) / User(s) -Creating a role in Keycloak has no effect on any user or group's permissions. To grant a set of permissions -to a user or group, we need to _attach_ the role to the user or group. To add a role to a user: +Creating a role in Keycloak has no effect on any user or group's permissions. To grant a set of permissions to a user or +group, we need to _attach_ the role to the user or group. To add a role to a user: 1. Select users on the left sidebar and enter the username in the Lookup searchbar. @@ -105,17 +198,17 @@ to a user or group, we need to _attach_ the role to the user or group. To add a ![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_user_role_mapping_add_role.png) -To attach a role to a group, follow the above steps by clicking on the groups tab and -selecting a group instead of selecting the user in the first step. +To attach a role to a group, follow the above steps by clicking on the groups tab and selecting a group instead of +selecting the user in the first step. -In the above section, we learned how to create a role with some attributes and attach it to a user or a group. -Now we will learn how to create scopes to grant a particular set of permissions to the user. +In the above section, we learned how to create a role with some attributes and attach it to a user or a group. Now we +will learn how to create scopes to grant a particular set of permissions to the user. :::note After the roles are assigned to a user or group in Keycloak, the user **must** logout and login back in to the service -for the roles to take in effect. For example let's say we add a set of roles for `conda-store` to the user named -"John Doe", now for the user "John Doe" to be able to avail newly granted/revoked roles, they need to login to -conda-store again (similarly for `jupyterhub` as well), after the roles are granted/revoked. +for the roles to take in effect. For example let's say we add a set of roles for `conda-store` to the user named "John +Doe", now for the user "John Doe" to be able to avail newly granted/revoked roles, they need to login to conda-store +again (similarly for `jupyterhub` as well), after the roles are granted/revoked. ::: ### Adding Users to a Group @@ -134,7 +227,8 @@ Repeat this process for all users who should be part of the group. ### Managing Subgroups -Subgroups allow you to create a hierarchical structure of groups, representing organizational units, projects, or teams. Subgroups inherit roles and attributes from their parent groups unless explicitly overridden. +Subgroups allow you to create a hierarchical structure of groups, representing organizational units, projects, or teams. +Subgroups inherit roles and attributes from their parent groups unless explicitly overridden. To manage subgroups: @@ -142,17 +236,22 @@ To manage subgroups: - **Assigning Roles to Subgroups**: Roles are not automatically inherited; assign roles to subgroups as needed. - **Use Cases**: Organize groups hierarchically to reflect organizational structures. -For more information, see the [Keycloak documentation on Group Hierarchies](https://www.keycloak.org/docs/latest/server_admin/#group-hierarchies). +For more information, see the +[Keycloak documentation on Group Hierarchies](https://www.keycloak.org/docs/latest/server_admin/#group-hierarchies). ## Managing Group Directories in JupyterHub :::note Important -As of version `2024.9.1`, JupyterHub creates and mounts directories only for groups with the `allow-group-directory-creation-role`. By default, this includes the `admin`, `analyst`, and `developer` groups. Previously, directories were automatically created for all Keycloak groups. This change gives administrators more control over shared directories and overall access management without cluttering the file system. +As of version `2024.9.1`, JupyterHub creates and mounts directories only for groups with the +`allow-group-directory-creation-role`. By default, this includes the `admin`, `analyst`, and `developer` groups. +Previously, directories were automatically created for all Keycloak groups. This change gives administrators more +control over shared directories and overall access management without cluttering the file system. ::: ### Understanding Group Directories -A group directory in JupyterHub is a shared folder accessible to all members of a specific group. It provides a shared space within the file system for collaboration. +A group directory in JupyterHub is a shared folder accessible to all members of a specific group. It provides a shared +space within the file system for collaboration. An example directory structure: @@ -171,7 +270,8 @@ An example directory structure: ### Assigning the `allow-group-directory-creation-role` to a Group -To enable directory creation and mounting for a group in JupyterHub, assign the `allow-group-directory-creation-role` to the group in Keycloak. +To enable directory creation and mounting for a group in JupyterHub, assign the `allow-group-directory-creation-role` to +the group in Keycloak. #### Steps to Assign the Role: @@ -192,19 +292,24 @@ To remove the group's directory access: - Under **Assigned Roles**, select `allow-group-directory-creation-role`. - Click **Remove selected**. -**Data Preservation:** No data is deleted when you remove the role; the directory is simply unmounted from users' JupyterLab sessions. +**Data Preservation:** No data is deleted when you remove the role; the directory is simply unmounted from users' +JupyterLab sessions. ### Managing Subgroup Directories -Subgroups can have their own directories in JupyterHub if assigned the `allow-group-directory-creation-role`. Assign the role to subgroups as needed to control access and collaboration. +Subgroups can have their own directories in JupyterHub if assigned the `allow-group-directory-creation-role`. Assign the +role to subgroups as needed to control access and collaboration. ## In-Depth Look at Roles and Groups -Understanding how roles and groups work together is essential for effectively managing access and permissions within Nebari. +Understanding how roles and groups work together is essential for effectively managing access and permissions within +Nebari. ### Groups -Groups represent collections of users who perform similar functions or belong to the same organizational unit. They simplify user management by allowing you to assign roles and permissions at the group level rather than individually to each user. +Groups represent collections of users who perform similar functions or belong to the same organizational unit. They +simplify user management by allowing you to assign roles and permissions at the group level rather than individually to +each user. By default, Nebari is deployed with the following groups: @@ -212,15 +317,18 @@ By default, Nebari is deployed with the following groups: - **developer**: Users who need access to development tools and environments. - **analyst**: Users who primarily analyze data and may have restricted access compared to developers. -Groups can be organized hierarchically using subgroups, allowing for more granular control and reflecting organizational structures. +Groups can be organized hierarchically using subgroups, allowing for more granular control and reflecting organizational +structures. :::info -**Shared Directories:** Users in a particular group will have access to that group's shared directory in JupyterHub if the group has the `allow-group-directory-creation-role` assigned. +**Shared Directories:** Users in a particular group will have access to that group's shared directory in JupyterHub if +the group has the `allow-group-directory-creation-role` assigned. ::: ### Roles -Roles define a set of permissions that grant access to specific resources or capabilities within Nebari's services. They are assigned to groups or users and determine what actions they can perform. +Roles define a set of permissions that grant access to specific resources or capabilities within Nebari's services. They +are assigned to groups or users and determine what actions they can perform. Roles can be of two types: @@ -237,16 +345,23 @@ Examples of roles include: Roles are assigned to groups, and users inherit those roles through their group memberships. This means that: -- **Users in a Group Get the Group's Roles**: If a group has certain roles assigned, all users in that group will inherit those roles. -- **Multiple Roles Can Be Assigned**: A group can have multiple roles from different clients, providing access to various services. -- **Roles Are Not Inherited by Subgroups**: Roles need to be explicitly assigned to subgroups if you want them to have specific permissions. +- **Users in a Group Get the Group's Roles**: If a group has certain roles assigned, all users in that group will + inherit those roles. +- **Multiple Roles Can Be Assigned**: A group can have multiple roles from different clients, providing access to + various services. +- **Roles Are Not Inherited by Subgroups**: Roles need to be explicitly assigned to subgroups if you want them to have + specific permissions. ### Role Hierarchies and Stacking -Roles can be designed to reflect hierarchical permissions. For example, an `admin` role may encompass all the permissions of a `developer` role. +Roles can be designed to reflect hierarchical permissions. For example, an `admin` role may encompass all the +permissions of a `developer` role. :::info -**Role Stacking:** If a user is a member of multiple groups with different roles, they effectively have the combined permissions of all assigned roles. For instance, if a user is in a group with the `conda_store_admin` role and another group with the `conda_store_viewer` role, the user will have administrative privileges in Conda-Store due to the higher permission level of `conda_store_admin`. +**Role Stacking:** If a user is a member of multiple groups with different roles, they effectively have the combined +permissions of all assigned roles. For instance, if a user is in a group with the `conda_store_admin` role and another +group with the `conda_store_viewer` role, the user will have administrative privileges in Conda-Store due to the higher +permission level of `conda_store_admin`. ::: ### Assigning Roles to Groups @@ -261,10 +376,14 @@ When creating or editing a group in Keycloak: ## Access Levels and Permissions -Roles on the other hand represent the type or category of user. This includes access and permissions that this category of user will need to perform their regular job duties. The differences between `groups` and `roles` are subtle. Particular roles (one or many), like `conda_store_admin`, are associated with a particular group, such as `admin` and any user in this group will then assume the role of `conda_store_admin`. +Roles on the other hand represent the type or category of user. This includes access and permissions that this category +of user will need to perform their regular job duties. The differences between `groups` and `roles` are subtle. +Particular roles (one or many), like `conda_store_admin`, are associated with a particular group, such as `admin` and +any user in this group will then assume the role of `conda_store_admin`. :::info -These roles can be stacked. This means that if a user is in one group with role `conda_store_admin` and another group with role `conda_store_viewer`, this user ultimately has the role `conda_store_admin`. +These roles can be stacked. This means that if a user is in one group with role `conda_store_admin` and another group +with role `conda_store_viewer`, this user ultimately has the role `conda_store_admin`. ::: Below is a summary of default groups, their access to Nebari resources, assigned roles, and permissions descriptions. @@ -277,16 +396,22 @@ Below is a summary of default groups, their access to Nebari resources, assigned | `superadmin` | | | | :::info -Check [Conda-store authorization model](https://conda-store.readthedocs.io/en/latest/contributing.html#authorization-model) for more details on conda-store authorization. +Check +[Conda-store authorization model](https://conda-store.readthedocs.io/en/latest/contributing.html#authorization-model) +for more details on conda-store authorization. ::: :::caution -The role `jupyterhub_admin` gives users elevated permissions to JupyterHub and should be applied judiciously. As mentioned in the table above, a JupyterHub admin is able to impersonate other users and view the contents of their home folder. For more details, read through the [JupyterHub documentation](https://z2jh.jupyter.org/en/stable/jupyterhub/customizing/user-management.html#admin-users). +The role `jupyterhub_admin` gives users elevated permissions to JupyterHub and should be applied judiciously. As +mentioned in the table above, a JupyterHub admin is able to impersonate other users and view the contents of their home +folder. For more details, read through the +[JupyterHub documentation](https://z2jh.jupyter.org/en/stable/jupyterhub/customizing/user-management.html#admin-users). ::: To create new groups or modify (or delete) existing groups, log in as `root` and click **Groups** on the left-hand side. -As an example, we create a new group named `conda-store-manager`. This group will have administrator access to the [Conda-Store service]. +As an example, we create a new group named `conda-store-manager`. This group will have administrator access to the +[Conda-Store service]. 1. Click **New** in the upper-right hand corner under **Groups**. @@ -296,28 +421,37 @@ As an example, we create a new group named `conda-store-manager`. This group wil ![Keycloak add group form - name field set to conda-store-manager](/img/how-tos/keycloak_new_group1.png) -2. Under **Role Mapping**, add the appropriate **Client Roles** as needed; there should be no need to update the **Realm Roles**. +2. Under **Role Mapping**, add the appropriate **Client Roles** as needed; there should be no need to update the **Realm + Roles**. ![Keycloak group conda-store-manager form - role mappings tab focused with expanded client roles dropdown](/img/how-tos/keycloak_new_group2.png) -In this example, the new group only has one mapped role, `conda_store_admin`; however, it's possible to attach multiple **Client Roles** to a single group. +In this example, the new group only has one mapped role, `conda_store_admin`; however, it's possible to attach multiple +**Client Roles** to a single group. ![Keycloak group conda-store-manager form - role mappings tab focused ](/img/how-tos/keycloak_new_group3.png) Once complete, return to the **Users** section in the dashboard and add the relevant users to this newly created group. +## Components and Scopes + +When specifying fine-grained permissions in Keycloak, we use the `components` and `scopes` attributes. These attributes +are used to define the permissions that a role grants to a user or group. This model follows the RBAC (Role-Based Access +Control) model and is based upong the same structure utilized by +[Jupyterhub](https://jupyterhub.readthedocs.io/en/latest/rbac/index.html). ### Components Attribute -We have seen in the above example the `component` attribute while creating a role. The value of this parameter -depends on the type of component in the service, we're creating a role for, currently we only have two components: +We have seen in the above example the `component` attribute while creating a role. The value of this parameter depends +on the type of component in the service, we're creating a role for, currently we only have two components: - `jupyterhub`: to create `jupyterhub` native roles in the `jupyterhub` client. - `conda-store`: to create `conda-store` roles in the `conda_store` client ### JupyterHub Scopes -The syntax for the `scopes` attribute for a `jupyterhub` role in Keycloak in Nebari follows the native RBAC scopes syntax -for JupyterHub itself. The documentation can be found [here](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#scope-conventions). +The syntax for the `scopes` attribute for a `jupyterhub` role in Keycloak in Nebari follows the native RBAC scopes +syntax for JupyterHub itself. The documentation can be found +[here](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#scope-conventions). As an example, scopes for allowing users to share apps in Nebari's `jhub-apps` launcher may look like this: @@ -329,8 +463,8 @@ The `scopes` defined above consists of three scopes: - `read:users:name`: grants permissions to read other user's names - `read:groups:name`: grants permissions to read other groups's names -To be able to share a server to a group or a user you need to be able to read other user's or group's names and must have -permissions to be able to share your server, this is what this set of permissions implement. +To be able to share a server to a group or a user you need to be able to read other user's or group's names and must +have permissions to be able to share your server, this is what this set of permissions implement. ### Conda Store Scopes From 5e17d9c00b7b41cda6ed600723a18b6b2a4e985f Mon Sep 17 00:00:00 2001 From: vinicius douglas cerutti Date: Fri, 24 Jan 2025 15:13:34 -0300 Subject: [PATCH 02/11] add markdown table component --- ...ssions.md => fine-grained-permissions.mdx} | 111 +++++++++++++++++- docs/package.json | 3 +- docs/src/components/MarkdownTable.ts | 37 ++++++ 3 files changed, 144 insertions(+), 7 deletions(-) rename docs/docs/how-tos/{fine-grained-permissions.md => fine-grained-permissions.mdx} (75%) create mode 100644 docs/src/components/MarkdownTable.ts diff --git a/docs/docs/how-tos/fine-grained-permissions.md b/docs/docs/how-tos/fine-grained-permissions.mdx similarity index 75% rename from docs/docs/how-tos/fine-grained-permissions.md rename to docs/docs/how-tos/fine-grained-permissions.mdx index 18e323288..50be6a620 100644 --- a/docs/docs/how-tos/fine-grained-permissions.md +++ b/docs/docs/how-tos/fine-grained-permissions.mdx @@ -56,6 +56,7 @@ the permission model to fit your precise security and governance requirements— ready-to-use defaults Nebari provides. ## Managing Roles and Permissions + Overview - *Roles*: Define a set of permissions (or scopes) for a particular client (service). - *Groups*: Aggregate users who share similar permissions. @@ -114,12 +115,110 @@ with role `conda_store_viewer`, this user ultimately has the role `conda_store_a Below is a table illustrating each default group, the resources they can access, and the roles they inherit. The table also includes a brief description of the permissions granted to each group. -| Group | Access to Nebari Resources | Roles | Permissions Description | -| ------------ | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `analyst` |
  • Conda-Store
  • Jupyterhub
  • Argo Workflows
  • Grafana
|
  • `conda_store_developer`
  • `jupyterhub_developer`
  • `argo_viewer`
  • `grafana_viewer`
|
  • Default user permissions
  • Access to start a server instance and generate JupyterHub access token.
  • Read/write access to shared `analyst` folder group mount
  • Read access to `analyst` and write access to personal conda-store namespace
  • Read access to Argo-Workflows and Jupyter-Scheduler
  • Inherent Grafana permissions from [Grafana viewer scopes](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#organization-roles)
| -| `developer` |
  • Conda-Store
  • Dask
  • Jupyterhub
  • Argo Workflows
  • Grafana Developer
|
  • `conda_store_developer`
  • `dask_developer`
  • `jupyterhub_developer`
  • `argo_developer`
  • `grafana_developer`
|
  • All of the above access, plus...
  • Read access `developer` conda-store namespace
  • Access to create Dask clusters.
  • Read/write access to shared `developer` folder group mount
  • Read/create access to Argo-Workflows and Jupyter-Scheduler
  • Inherent Grafana permissions from [Grafana editor scopes](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#organization-roles)
| -| `admin` |
  • Conda-Store
  • Dask
  • Jupyterhub
  • Argo Workflows
  • Grafana
|
  • `conda_store_admin`
  • `dask_admin`
  • `jupyterhub_admin`
  • `argo_admin`
  • `grafana_admin`
|
  • All of the above access, plus...
  • Read/write access to all conda-store available namespaces/environments.
  • Access to Jupyterhub Admin page and can access JupyterLab users spaces
  • Access to Keycloak and can add remove users and groups
  • Inherent Grafana permissions from [Grafana administrator scopes](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#organization-roles)
| -| `superadmin` |
  • Conda-Store
  • Dask
  • Jupyterhub
  • Argo Workflows
  • Grafana
|
  • `conda_store_superadmin`
  • `dask_admin`
  • `jupyterhub_admin`
  • `argo_admin`
  • `realm_admin` (Keycloak)
  • `grafana_admin`
|
  • All of the above access, plus...
  • Delete (build and environment) access on conda-store
  • Full access to Keycloak (realm) (same as `root`)
| +import React from 'react'; + +export const GroupTable = ({ groups }) => { + return ( + + + + + + + + + + + {groups.map((group) => ( + + {/* 1. Resources */} + + + {/* 2. Roles */} + + + {/* 3. Permissions Description */} + + + ))} + +
GroupAccess to Nebari ResourcesRolesPermissions Description
+
    + {group.resources.map((resource) => ( +
  • {resource}
  • + ))} +
+
+
    + {group.roles.map((role) => ( +
  • {role}
  • + ))} +
+
+
    + {group.permissions.map((perm, i) => ( +
  • {perm}
  • + ))} +
+
+ ); +}; + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import MarkdownTable from @site/src/components/MardownTable; + +
+ + + + + +| Group | Access to Nebari Resources | Roles | Permissions Description | +| --------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `analyst` |
  • Conda-Store
  • Jupyterhub
  • Argo Workflows
  • Grafana
|
  • `conda_store_developer`
  • `jupyterhub_developer`
  • `argo_viewer`
  • `grafana_viewer`
|
  • Default user permissions.
  • Access to start a server instance and generate a JupyterHub access token.
  • Read/write access to shared `analyst` folder group mount.
  • Read access to the `analyst` namespace in conda-store (write access to personal namespace).
  • Read access to Argo Workflows and Jupyter Scheduler.
  • Inherits Grafana viewer permissions from [Grafana viewer scopes](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#organization-roles).
| + +
+ + +| Group | Access to Nebari Resources | Roles | Permissions Description | +| ----------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `developer` |
  • Conda-Store
  • Dask
  • Jupyterhub
  • Argo Workflows
  • Grafana Developer
|
  • `conda_store_developer`
  • `dask_developer`
  • `jupyterhub_developer`
  • `argo_developer`
  • `grafana_developer`
|
  • Includes all `analyst` access, plus...
  • Read access to the `developer` namespace in conda-store.
  • Ability to create Dask clusters.
  • Read/write access to the shared `developer` folder group mount.
  • Read/create access to Argo Workflows and Jupyter Scheduler.
  • Inherits Grafana editor permissions from [Grafana editor scopes](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#organization-roles).
| + +
+ + +| Group | Access to Nebari Resources | Roles | Permissions Description | +| ------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `admin` |
  • Conda-Store
  • Dask
  • Jupyterhub
  • Argo Workflows
  • Grafana
|
  • `conda_store_admin`
  • `dask_admin`
  • `jupyterhub_admin`
  • `argo_admin`
  • `grafana_admin`
|
  • Includes all `developer` access, plus...
  • Read/write access to all conda-store namespaces/environments.
  • Access to JupyterHub Admin page and ability to access users’ JupyterLab spaces.
  • Access to Keycloak for user and group management.
  • Inherits Grafana administrator permissions from [Grafana administrator scopes](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#organization-roles).
| + +
+ + +| Group | Access to Nebari Resources | Roles | Permissions Description | +| ------------ | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `superadmin` |
  • Conda-Store
  • Dask
  • Jupyterhub
  • Argo Workflows
  • Grafana
|
  • `conda_store_superadmin`
  • `dask_admin`
  • `jupyterhub_admin`
  • `argo_admin`
  • `realm_admin` (Keycloak)
  • `grafana_admin`
|
  • Includes all `admin` access, plus...
  • Delete (build and environment) access in conda-store.
  • Full realm administration in Keycloak (same as `root`).
| + +
+
+ +
+ + | :::info Check diff --git a/docs/package.json b/docs/package.json index c170eba3b..7d58403ae 100644 --- a/docs/package.json +++ b/docs/package.json @@ -50,7 +50,8 @@ "raw-loader": "^4.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", - "sass": "^1.77.8" + "sass": "^1.77.8", + "react-markdown": "^8.0.7" }, "devDependencies": { "@babel/eslint-parser": "^7.25.1", diff --git a/docs/src/components/MarkdownTable.ts b/docs/src/components/MarkdownTable.ts new file mode 100644 index 000000000..73463525c --- /dev/null +++ b/docs/src/components/MarkdownTable.ts @@ -0,0 +1,37 @@ +import React, { FC } from 'react'; +import ReactMarkdown from 'react-markdown'; + +interface MarkdownTableProps { + headers: string[]; + rows: string[][]; +} + +export const MarkdownTable: FC = ({ headers, rows }) => { + return ( + + + + { + headers.map((headerText, headerIdx) => ( + + )) + } + + + + { + rows.map((rowData, rowIdx) => ( + + { + rowData.map((cellContent, cellIdx) => ( + + )) + } + + ))} + +
{ headerText }
+ { cellContent } +
+ ); +}; From bc3c18b2f5c9e73328909b1d3ac55de0c42f2bb1 Mon Sep 17 00:00:00 2001 From: vinicius douglas cerutti Date: Fri, 24 Jan 2025 23:50:03 -0300 Subject: [PATCH 03/11] add table component and refactor scopes section with detailed info --- .../docs/how-tos/fine-grained-permissions.mdx | 362 +++++++++++++++--- docs/package.json | 5 +- docs/src/components/MarkdownTable.ts | 37 -- docs/src/components/MarkdownTable/index.tsx | 46 +++ 4 files changed, 364 insertions(+), 86 deletions(-) delete mode 100644 docs/src/components/MarkdownTable.ts create mode 100644 docs/src/components/MarkdownTable/index.tsx diff --git a/docs/docs/how-tos/fine-grained-permissions.mdx b/docs/docs/how-tos/fine-grained-permissions.mdx index 50be6a620..afe4ecbe3 100644 --- a/docs/docs/how-tos/fine-grained-permissions.mdx +++ b/docs/docs/how-tos/fine-grained-permissions.mdx @@ -54,8 +54,22 @@ However, certain environments require more specialized or granular access contro any user with appropriate privileges) can create custom roles and scopes directly in Keycloak. This allows you to tailor the permission model to fit your precise security and governance requirements—all while still benefiting from the ready-to-use defaults Nebari provides. +title: Fine-Grained Permissions via Keycloak +description: Learn how to manage roles, groups, and scopes in Keycloak to grant fine-grained permissions in Nebari. +--- +Nebari uses [Keycloak](https://www.keycloak.org/) to centrally manage roles, +permissions, and scopes for its core services like `jupyterhub` and `conda-store`. By +default, Nebari provides: + +- **Predefined roles** (e.g., `conda_store_admin`, `jupyterhub_developer`) +- **Default groups** (`admin`, `developer`, `analyst`, `superadmin`) +- **Clients** corresponding to key Nebari services + +These defaults cover most common access requirements. However, when more specific or +granular access control is needed, administrators can create custom roles and scopes in +Keycloak, see [Creating Custom Roles with Scopes](#creating-custom-roles-with-scopes). -## Managing Roles and Permissions +## Default Clients and Groups in Nebari Overview - *Roles*: Define a set of permissions (or scopes) for a particular client (service). @@ -73,6 +87,8 @@ integrations: By default, Nebari comes with several custom clients included in a fresh deployment. These clients facilitate various services and integrations within the Nebari ecosystem. The predefined clients are as follows: +A fresh Nebari deployment comes with several custom Keycloak clients (services) enabled. +You can manage these from **Clients** in the Keycloak admin console: ```yaml clients: @@ -163,59 +179,168 @@ export const GroupTable = ({ groups }) => { ); }; +Users are organized into default groups: `admin`, `developer`, `analyst`, and +`superadmin`. Each group has one or more default roles that define their access level +across Nebari services. + +:::info Shared Folders +Membership in one of the default groups also grants access to the corresponding shared +folder (e.g., the `developer` group has access to `~/shared/developer`). Since, +`2024.9.1` Nebari now required a special role to access the shared folders +`Allow-group-directory-creation-role`. +::: -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import MarkdownTable from @site/src/components/MardownTable; - -
+## In-depth Look at Default Roles and Groups - - - +Groups represent a collection of users that perform similar actions and therefore +require similar permissions. By default, Nebari is deployed with the following groups: +`admin`, `developer`, and `analyst` (in roughly descending order of permissions and +scope). -| Group | Access to Nebari Resources | Roles | Permissions Description | -| --------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `analyst` |
  • Conda-Store
  • Jupyterhub
  • Argo Workflows
  • Grafana
|
  • `conda_store_developer`
  • `jupyterhub_developer`
  • `argo_viewer`
  • `grafana_viewer`
|
  • Default user permissions.
  • Access to start a server instance and generate a JupyterHub access token.
  • Read/write access to shared `analyst` folder group mount.
  • Read access to the `analyst` namespace in conda-store (write access to personal namespace).
  • Read access to Argo Workflows and Jupyter Scheduler.
  • Inherits Grafana viewer permissions from [Grafana viewer scopes](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#organization-roles).
| - -
- +Roles on the other hand represent the type or category of user. This includes access and +permissions that this category of user will need to perform their regular job duties. +The differences between `groups` and `roles` are subtle. Particular roles (one or many), +like `conda_store_admin`, are associated with a particular group, such as `admin` and +any user in this group will then assume the role of `conda_store_admin`. -| Group | Access to Nebari Resources | Roles | Permissions Description | -| ----------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `developer` |
  • Conda-Store
  • Dask
  • Jupyterhub
  • Argo Workflows
  • Grafana Developer
|
  • `conda_store_developer`
  • `dask_developer`
  • `jupyterhub_developer`
  • `argo_developer`
  • `grafana_developer`
|
  • Includes all `analyst` access, plus...
  • Read access to the `developer` namespace in conda-store.
  • Ability to create Dask clusters.
  • Read/write access to the shared `developer` folder group mount.
  • Read/create access to Argo Workflows and Jupyter Scheduler.
  • Inherits Grafana editor permissions from [Grafana editor scopes](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#organization-roles).
| +:::tip Technical Note +Roles in nebari can stack, meaning that if a user is in one group with role +`conda_store_admin` and another group with role `conda_store_viewer`, this user +ultimately has the role `conda_store_admin`. Also, in case of different level of access +orignating from different groups, precedence is given to the highest level of access. +::: -
- +Below is a table that outlines the default roles, groups, and permissions that come with +Nebari: -| Group | Access to Nebari Resources | Roles | Permissions Description | -| ------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `admin` |
  • Conda-Store
  • Dask
  • Jupyterhub
  • Argo Workflows
  • Grafana
|
  • `conda_store_admin`
  • `dask_admin`
  • `jupyterhub_admin`
  • `argo_admin`
  • `grafana_admin`
|
  • Includes all `developer` access, plus...
  • Read/write access to all conda-store namespaces/environments.
  • Access to JupyterHub Admin page and ability to access users’ JupyterLab spaces.
  • Access to Keycloak for user and group management.
  • Inherits Grafana administrator permissions from [Grafana administrator scopes](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#organization-roles).
| +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; -
- +import { Table } from '@site/src/components/MarkdownTable'; -| Group | Access to Nebari Resources | Roles | Permissions Description | -| ------------ | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `superadmin` |
  • Conda-Store
  • Dask
  • Jupyterhub
  • Argo Workflows
  • Grafana
|
  • `conda_store_superadmin`
  • `dask_admin`
  • `jupyterhub_admin`
  • `argo_admin`
  • `realm_admin` (Keycloak)
  • `grafana_admin`
|
  • Includes all `admin` access, plus...
  • Delete (build and environment) access in conda-store.
  • Full realm administration in Keycloak (same as `root`).
| +
- + + + + + +
+ + +
+ + +
+ - | @@ -231,12 +356,57 @@ The role `jupyterhub_admin` gives users elevated permissions to JupyterHub and s mentioned in the table above, a JupyterHub admin is able to impersonate other users and view the contents of their home folder. For more details, read through the [JupyterHub documentation](https://z2jh.jupyter.org/en/stable/jupyterhub/customizing/user-management.html#admin-users). +:::note +See [Conda-Store Authorization +Model](https://conda-store.readthedocs.io/en/latest/contributing.html#authorization-model) +for additional details. ::: -To create new groups or modify (or delete) existing groups, log in as `root` and click **Groups** on the left-hand side. +:::warning +The `jupyterhub_admin` role grants permission to impersonate other users, including +viewing their home folder contents. Use it sparingly and refer to the [JupyterHub +docs](https://z2jh.jupyter.org/en/stable/jupyterhub/customizing/user-management.html#admin-users) +for more info. +::: + +## Roles, Groups, and Scopes As an example, we create a new group named `conda-store-manager`. This group will have administrator access to the [Conda-Store service]. +When managing user access in Nebari, it's helpful to understand how **roles**, +**groups**, and **scopes** work together: + +- **Roles**: A named set of permissions, while also providing a way to categorize users + by their access level or job function. Example: `conda_store_developer` or + `jupyterhub_admin`. +- **Groups**: Collections of users who need similar permissions. For example, the + `developer` group might contain multiple roles like `conda_store_developer` and + `jupyterhub_developer`. +- **Scopes**: A Service's fine-grained permission entity within a role. For instance, a + JupyterHub scope might be `read:users:name` (to read other users’ names), while a + Conda-Store scope might be `admin!namespace=analyst` (to grant admin-level access in + the `analyst` namespace). + +Whenever you create or modify a role in Keycloak, it won’t affect anyone until it’s +assigned to a group or user. Below is the general process: + +**Typical Workflow**: +1. **Create or edit** a role (and define its scopes) under the relevant **Client** + (e.g., `jupyterhub`, `conda_store`) in Keycloak. +2. **Assign** this role to a **group** (or directly to an individual user). +3. **Add users** to that group if you haven’t already. + +:::note +Users must log out and log back in for newly assigned roles to take effect. +::: + +### Granting Permissions to a User + +Let’s say you have someone who only needs *administrator-level privileges for +Conda-Store*, while leaving other services at their default access. + +As an example, we create a new group named `conda-store-manager`. This group will have +administrator access to the `Conda-Store` service. 1. Click **New** in the upper-right hand corner under **Groups**. @@ -253,17 +423,102 @@ As an example, we create a new group named `conda-store-manager`. This group wil In this example, the new group only has one mapped role, `conda_store_admin`; however, it's possible to attach multiple **Client Roles** to a single group. +2. Under **Role Mapping**, add the appropriate **Client Roles** as needed; there should + be no need to update the **Realm Roles**. + +![Keycloak group conda-store-manager form - role mappings tab focused with expanded client roles dropdown](/img/how-tos/keycloak_new_group2.png) + +In this example, the new group only has one mapped role, `conda_store_admin`; however, +it's possible to attach multiple **Client Roles** to a single group. ![Keycloak group conda-store-manager form - role mappings tab focused ](/img/how-tos/keycloak_new_group3.png) -Once complete, return to the **Users** section in the dashboard and add the relevant users to this newly created group. +Once complete, return to the **Users** section in the dashboard and add the relevant +users to this newly created group. + +:::note +To create new groups or modify (or delete) existing groups, log in as `root` and click +**Groups** on the left-hand side. +::: + +### Creating Custom Roles with Scopes + +Beyond Nebari’s default roles, you can also create your own with highly specific +permissions. The process for creating a role is similar, irrespective of the service. + +:::warning +Nebari currently only supports custom roles for the `jupyterhub` and `conda_store`. +Future releases may extend this to other services. +::: + +#### Components and Syntax + +In Nebari, **scopes** are closely tied to the service (client) you’re working +with— In Keycloak's roles we identify them as “components.” + +- **Components**: + Specifies the service that a role’s scopes apply to (e.g., `jupyterhub` or `conda-store`). +- **Scopes**: + The actual permission strings recognized by that service. + +
+ +#### JupyterHub Scopes + +JupyterHub scopes syntax in Nebari follow [JupyterHub’s built-in RBAC +syntax](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html). For example: +``` +shares!user,read:users:name,read:groups:name +``` +- `shares!user`: Allows sharing servers with other users. +- `read:users:name`: Grants read access to other users’ names. +- `read:groups:name`: Grants read access to other groups’ names. + +This combination allows a user to share their server with others (via `shares!user`) and +also read other users’ and groups’ names. + +For a complete list of JupyterHub scopes, see the +[https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#available-scopes](JupyterHub +documentation). + +Nebari extend these scoped including a new *share* scope that is reserved for +`Jhub-apps` usage, allowing, as seen above for sharing servers with other users, +including more specifcaly jhub-apps applications (apps). + +--- + +#### Conda-Store Scopes + +Conda-Store scopes are defined at the namespace level. For example: +``` +admin!namespace=analyst,developer!namespace=nebari-git +``` +- `admin!namespace=analyst`: Grants an **admin** role in the `analyst` namespace. +- `developer!namespace=nebari-git`: Grants a **developer** role in the `nebari-git` namespace. + +This grants an **admin** role in the `analyst` namespace, plus a **developer** role in +the `nebari-git` namespace. When assigned to a user or group, these permissions apply +only to those specified namespaces in Conda-Store. + +Conda-store scopes follow a much simpler syntax than JupyterHub scopes, as they are only +defined at the namespace level: +``` +!namespace= +``` +whereas the `access_level` there entices the available conda-store' roles as per its own +documentation +[Conda-Store +role-mappings](https://conda.store/conda-store/explanations/conda-store-concepts#role-mappings). + +
### Creating a Role The process for creating a role is similar, irrespective of the service. To create a role for a service +In the following example, we create a new role for the `JupyterHub` client granting the +same level of permission outlined in the section above [JupyterHub Scopes](#jupyterhub-scopes). 1. Select the appropriate client and click on "Add Role". - ![Keycloak client add jupyterhub role](/img/how-tos/keycloak_jupyterhub_client.png) 2. On the "Add Role" form, write a meaningful name and description for the role. Be sure to include what this role @@ -579,3 +834,16 @@ The `scopes` defined above consists of two scopes: - `developer!namespace=nebari-git`: grants `developer` access to namespace `nebari-git` When attached to a user or a group, the above-mentioned permissions will be granted to the user/group. +1. On the "Add Role" form, write a meaningful name and description for the role. Be sure + to include what this role intends to accomplish. Click "Save". + +![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_jupyterhub_add_role.png) + +3. Now the role has been created, but it does nothing. Let's add some permissions to it + by clicking on the "Attributes" tab and adding scopes. The following sections will + explain the `components` and `scopes` in more detail. + + ![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_add_role_attributes.png) + +To make these new permissions effective, assign the custom role to a user or group (via +**Role Mappings**). Again, any user changes require a re-login to become active. diff --git a/docs/package.json b/docs/package.json index 7d58403ae..774fb6ccb 100644 --- a/docs/package.json +++ b/docs/package.json @@ -44,14 +44,15 @@ "@docusaurus/preset-classic": "3.5.0", "@mdx-js/react": "^3.0.0", "clsx": "^2.1.1", + "dedent": "^0.7.0", "docusaurus-lunr-search": "^3.3.0", "docusaurus-plugin-sass": "^0.2.5", "prism-react-renderer": "^2.1.0", "raw-loader": "^4.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", - "sass": "^1.77.8", - "react-markdown": "^8.0.7" + "react-markdown": "^8.0.7", + "sass": "^1.77.8" }, "devDependencies": { "@babel/eslint-parser": "^7.25.1", diff --git a/docs/src/components/MarkdownTable.ts b/docs/src/components/MarkdownTable.ts deleted file mode 100644 index 73463525c..000000000 --- a/docs/src/components/MarkdownTable.ts +++ /dev/null @@ -1,37 +0,0 @@ -import React, { FC } from 'react'; -import ReactMarkdown from 'react-markdown'; - -interface MarkdownTableProps { - headers: string[]; - rows: string[][]; -} - -export const MarkdownTable: FC = ({ headers, rows }) => { - return ( -
- - - { - headers.map((headerText, headerIdx) => ( - - )) - } - - - - { - rows.map((rowData, rowIdx) => ( - - { - rowData.map((cellContent, cellIdx) => ( - - )) - } - - ))} - -
{ headerText }
- { cellContent } -
- ); -}; diff --git a/docs/src/components/MarkdownTable/index.tsx b/docs/src/components/MarkdownTable/index.tsx new file mode 100644 index 000000000..eb882aff8 --- /dev/null +++ b/docs/src/components/MarkdownTable/index.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import ReactMarkdown from 'react-markdown'; +import dedent from 'dedent'; + +/** + * Each row in the table is an array of cells. + * Each cell can be either: + * - A single Markdown string, OR + * - An array of Markdown lines (which we'll join with '\n'). + */ +export interface MarkdownTableProps { + headers: string[]; + rows: Array>; +} + +export function Table({ headers, rows }: MarkdownTableProps) { + return ( + + + + {headers.map((header, headerIdx) => ( + + ))} + + + + {rows.map((rowData, rowIdx) => ( + + {rowData.map((cellData, cellIdx) => { + // If the cellData is an array of strings, join them with "\n" + const cellContent = Array.isArray(cellData) + ? dedent(cellData.join('\n')) + : dedent(cellData); + + return ( + + ); + })} + + ))} + +
{header}
+ {cellContent} +
+ ); +} From f70e70b0ab7943aec826db41cfc96574fd75368b Mon Sep 17 00:00:00 2001 From: vinicius douglas cerutti Date: Wed, 29 Jan 2025 17:43:55 -0300 Subject: [PATCH 04/11] fix lint typo --- docs/docs/how-tos/fine-grained-permissions.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/how-tos/fine-grained-permissions.mdx b/docs/docs/how-tos/fine-grained-permissions.mdx index afe4ecbe3..158c274d5 100644 --- a/docs/docs/how-tos/fine-grained-permissions.mdx +++ b/docs/docs/how-tos/fine-grained-permissions.mdx @@ -207,7 +207,7 @@ any user in this group will then assume the role of `conda_store_admin`. Roles in nebari can stack, meaning that if a user is in one group with role `conda_store_admin` and another group with role `conda_store_viewer`, this user ultimately has the role `conda_store_admin`. Also, in case of different level of access -orignating from different groups, precedence is given to the highest level of access. +originating from different groups, precedence is given to the highest level of access. ::: Below is a table that outlines the default roles, groups, and permissions that come with From b9a49667944f2137b2b187df4144267f36148a8e Mon Sep 17 00:00:00 2001 From: "Vinicius D. Cerutti" <51954708+viniciusdc@users.noreply.github.com> Date: Wed, 26 Feb 2025 16:43:39 -0300 Subject: [PATCH 05/11] Include extra permissions for API conda-store token --- docs/docs/how-tos/fine-grained-permissions.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/how-tos/fine-grained-permissions.mdx b/docs/docs/how-tos/fine-grained-permissions.mdx index 158c274d5..478196bae 100644 --- a/docs/docs/how-tos/fine-grained-permissions.mdx +++ b/docs/docs/how-tos/fine-grained-permissions.mdx @@ -334,6 +334,7 @@ import { Table } from '@site/src/components/MarkdownTable'; [ '- Includes all Admin permissions, plus...', '- Delete privileges in Conda-Store (build & environment).', + '- Admin API service token privileges in Conda-Store.', '- Full Keycloak realm administration (root-level).', ], ], From bf80211f129daf1d85fe1c570844797660234035 Mon Sep 17 00:00:00 2001 From: vinicius douglas cerutti Date: Tue, 4 Nov 2025 16:11:34 -0300 Subject: [PATCH 06/11] add mention of conda-store default mapping --- .../docs/how-tos/fine-grained-permissions.mdx | 26 +++++++++ docs/src/theme/TOCItems/Tree.tsx | 54 +++++++++++++++++++ docs/src/theme/TOCItems/index.tsx | 54 +++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 docs/src/theme/TOCItems/Tree.tsx create mode 100644 docs/src/theme/TOCItems/index.tsx diff --git a/docs/docs/how-tos/fine-grained-permissions.mdx b/docs/docs/how-tos/fine-grained-permissions.mdx index 478196bae..2b6fddf20 100644 --- a/docs/docs/how-tos/fine-grained-permissions.mdx +++ b/docs/docs/how-tos/fine-grained-permissions.mdx @@ -513,6 +513,32 @@ role-mappings](https://conda.store/conda-store/explanations/conda-store-concepts
+:::note Conda-Store defaults and Nebari’s internal group handling +Nebari grants some Conda-Store permissions **automatically**, regardless of how your +Keycloak groups are configured. The authentication class applies these defaults at +login so users can work even if the Keycloak groups don’t define any `conda_store_*` +client roles. + +**What Nebari applies by default** +- **Personal namespace** → always **admin** + `"{username}/*" → {"admin"}` (cannot be downgraded by scopes). +- **Deployment default namespace** → **viewer** + `"{default_namespace}/*" → {"viewer"}`. +- **Nebari default groups** (`analyst`, `developer`, `admin`) → **[handled internally](https://github.com/nebari-dev/nebari/blob/main/src/_nebari/stages/kubernetes_services/template/modules/kubernetes/services/conda-store/config/conda_store_config.py#L105)** + If a user is in one of these groups, Nebari binds a **same-named namespace** to the + user with that group’s effective Conda-Store role (e.g., group `analyst` ⇒ `analyst/*`), + **even when the Keycloak group itself has no client roles**. +- **SuperAdmin** (`conda_store_superadmin`) → full wildcard + `"*/*" → {"admin"}` (includes delete & service-token privileges). + +**How it reconciles with Keycloak scopes** +- On each login, Nebari **removes non-default bindings** and re-applies permissions from + Keycloak role **scopes** (e.g., `admin!namespace=analyst`). +- If multiple grants target the same namespace, the **highest** permission wins. +- Order of precedence (highest → lowest): **SuperAdmin** → **explicit scopes** → **internal defaults**. +- Changes take effect after the user signs in again. +::: + ### Creating a Role The process for creating a role is similar, irrespective of the service. To create a role for a service diff --git a/docs/src/theme/TOCItems/Tree.tsx b/docs/src/theme/TOCItems/Tree.tsx new file mode 100644 index 000000000..05f85ed6b --- /dev/null +++ b/docs/src/theme/TOCItems/Tree.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import Link from '@docusaurus/Link'; +import type { Props } from '@theme/TOCItems/Tree'; + +function TOCItemTree({ + toc, + className, + linkClassName, + isChild, +}: Props): JSX.Element | null { + if (!toc.length) { + return null; + } + + return ( +
    + {toc.map((heading) => { + // Parse the heading ID to check if it has "provider::actualId" + let providerQuery = null; + let actualId = heading.id; + + if (heading.id.includes('::')) { + const [provider, ...rest] = heading.id.split('::'); + providerQuery = provider; + actualId = rest.join('::'); // In case there's more than one '::', join back the rest. + } + + // If a provider is found, build the URL with the query param + const linkHref = providerQuery + ? `?provider=${providerQuery}#${heading.id}` + : `#${heading.id}`; + + return ( +
  • + + +
  • + ); + })} +
+ ); +} + +export default React.memo(TOCItemTree); diff --git a/docs/src/theme/TOCItems/index.tsx b/docs/src/theme/TOCItems/index.tsx new file mode 100644 index 000000000..b61bcd1ef --- /dev/null +++ b/docs/src/theme/TOCItems/index.tsx @@ -0,0 +1,54 @@ +import React, {useMemo} from 'react'; +import {useThemeConfig} from '@docusaurus/theme-common'; +import { + useTOCHighlight, + useFilteredAndTreeifiedTOC, + type TOCHighlightConfig, +} from '@docusaurus/theme-common/internal'; +import TOCItemTree from '@theme/TOCItems/Tree'; +import type {Props} from '@theme/TOCItems'; + +export default function TOCItems({ + toc, + className = 'table-of-contents table-of-contents__left-border', + linkClassName = 'table-of-contents__link', + linkActiveClassName = undefined, + minHeadingLevel: minHeadingLevelOption, + maxHeadingLevel: maxHeadingLevelOption, + ...props +}: Props): JSX.Element | null { + const themeConfig = useThemeConfig(); + + const minHeadingLevel = + minHeadingLevelOption ?? themeConfig.tableOfContents.minHeadingLevel; + const maxHeadingLevel = + maxHeadingLevelOption ?? themeConfig.tableOfContents.maxHeadingLevel; + + const tocTree = useFilteredAndTreeifiedTOC({ + toc, + minHeadingLevel, + maxHeadingLevel, + }); + + const tocHighlightConfig: TOCHighlightConfig | undefined = useMemo(() => { + if (linkClassName && linkActiveClassName) { + return { + linkClassName, + linkActiveClassName, + minHeadingLevel, + maxHeadingLevel, + }; + } + return undefined; + }, [linkClassName, linkActiveClassName, minHeadingLevel, maxHeadingLevel]); + useTOCHighlight(tocHighlightConfig); + + return ( + + ); +} From 9e72f409de1488f5c9429080b9154c365fe97709 Mon Sep 17 00:00:00 2001 From: vinicius douglas cerutti Date: Sat, 24 Jan 2026 14:24:13 -0300 Subject: [PATCH 07/11] address merge conflic changes --- .../docs/how-tos/fine-grained-permissions.mdx | 589 ++++-------------- 1 file changed, 136 insertions(+), 453 deletions(-) diff --git a/docs/docs/how-tos/fine-grained-permissions.mdx b/docs/docs/how-tos/fine-grained-permissions.mdx index 2b6fddf20..5f2430331 100644 --- a/docs/docs/how-tos/fine-grained-permissions.mdx +++ b/docs/docs/how-tos/fine-grained-permissions.mdx @@ -44,37 +44,26 @@ To create a new group in Keycloak: If you wish to organize your groups hierarchically, you can create subgroups by selecting a parent group and adding a subgroup. -# Fine Grained Permissions via Keycloak - -Nebari uses Keycloak to centrally manage roles, permissions, and scopes for various services like `jupyterhub` and -`conda-store`. By default, Nebari provides a set of predefined clients and default roles that cover common needs—such as -basic view/read permissions or full admin control, a full list of which can be found in the [] section. - -However, certain environments require more specialized or granular access controls. In these cases, administrators (or -any user with appropriate privileges) can create custom roles and scopes directly in Keycloak. This allows you to tailor -the permission model to fit your precise security and governance requirements—all while still benefiting from the -ready-to-use defaults Nebari provides. -title: Fine-Grained Permissions via Keycloak -description: Learn how to manage roles, groups, and scopes in Keycloak to grant fine-grained permissions in Nebari. ---- -Nebari uses [Keycloak](https://www.keycloak.org/) to centrally manage roles, -permissions, and scopes for its core services like `jupyterhub` and `conda-store`. By -default, Nebari provides: +## Fine-Grained Permissions via Keycloak + +Nebari uses [Keycloak](https://www.keycloak.org/) to centrally manage roles, permissions, and scopes for its core +services like `jupyterhub` and `conda-store`. By default, Nebari provides: - **Predefined roles** (e.g., `conda_store_admin`, `jupyterhub_developer`) - **Default groups** (`admin`, `developer`, `analyst`, `superadmin`) - **Clients** corresponding to key Nebari services -These defaults cover most common access requirements. However, when more specific or -granular access control is needed, administrators can create custom roles and scopes in -Keycloak, see [Creating Custom Roles with Scopes](#creating-custom-roles-with-scopes). +These defaults cover most common access requirements. However, when more specific or granular access control is needed, +administrators can create custom roles and scopes in Keycloak—see +[Creating Custom Roles with Scopes](#creating-custom-roles-with-scopes). ## Default Clients and Groups in Nebari Overview -- *Roles*: Define a set of permissions (or scopes) for a particular client (service). -- *Groups*: Aggregate users who share similar permissions. -- *Scopes*: The individual permissions (e.g., “read:users:name” for reading user names) that can be attached to a role. + +- **Roles**: Define a set of permissions (or scopes) for a particular client (service). +- **Groups**: Aggregate users who share similar permissions. +- **Scopes**: The individual permissions (e.g., `read:users:name`) that can be attached to a role. By creating custom roles with specific scopes, and assigning them to groups or users, you can fine-tune exactly who can do what in your Nebari environment. More details on how to create roles and scopes can be found in the @@ -82,13 +71,8 @@ do what in your Nebari environment. More details on how to create roles and scop ## Default Clients and permissions in Nebari -Nebari comes with several custom Keycloak clients in a fresh deployment. These map to key Nebari services and -integrations: - -By default, Nebari comes with several custom clients included in a fresh deployment. These clients facilitate various -services and integrations within the Nebari ecosystem. The predefined clients are as follows: -A fresh Nebari deployment comes with several custom Keycloak clients (services) enabled. -You can manage these from **Clients** in the Keycloak admin console: +A fresh Nebari deployment comes with several custom Keycloak clients (services) enabled. You can manage these from +**Clients** in the Keycloak admin console: ```yaml clients: @@ -97,7 +81,7 @@ clients: - grafana (if monitoring is enabled) - argo-server-sso (if argo is enabled) - forwardauth -``` +```` To manage and configure these clients, you can navigate to the `Clients` tab within the Keycloak admin console, as illustrated in the image below. @@ -110,108 +94,26 @@ Groups represent a collection of users that perform similar actions and therefor default, Nebari is deployed with the following groups: `admin`, `developer`, and `analyst` (in roughly descending order of permissions and scope). -:::info -Users in a particular group will also get access to that groups shared folder. So if `user A` belongs to the -`developer`, they will also have access to the `~/shared/developer` folder. This also applies to new groups that you -create. -::: - Roles on the other hand represent the type or category of user. This includes access and permissions that this category of user will need to perform their regular job duties. The differences between `groups` and `roles` are subtle. Particular roles (one or many), like `conda_store_admin`, are associated with a particular group, such as `admin` and any user in this group will then assume the role of `conda_store_admin`. -:::info -These roles can be stacked. This means that if a user is in one group with role `conda_store_admin` and another group -with role `conda_store_viewer`, this user ultimately has the role `conda_store_admin`. -::: - -## In-depth look at Roles and Groups - -Below is a table illustrating each default group, the resources they can access, and the roles they inherit. The table -also includes a brief description of the permissions granted to each group. - -import React from 'react'; - -export const GroupTable = ({ groups }) => { - return ( - - - - - - - - - - - {groups.map((group) => ( - - {/* 1. Resources */} - - - {/* 2. Roles */} - - - {/* 3. Permissions Description */} - - - ))} - -
GroupAccess to Nebari ResourcesRolesPermissions Description
-
    - {group.resources.map((resource) => ( -
  • {resource}
  • - ))} -
-
-
    - {group.roles.map((role) => ( -
  • {role}
  • - ))} -
-
-
    - {group.permissions.map((perm, i) => ( -
  • {perm}
  • - ))} -
-
- ); -}; -Users are organized into default groups: `admin`, `developer`, `analyst`, and -`superadmin`. Each group has one or more default roles that define their access level -across Nebari services. - :::info Shared Folders -Membership in one of the default groups also grants access to the corresponding shared -folder (e.g., the `developer` group has access to `~/shared/developer`). Since, -`2024.9.1` Nebari now required a special role to access the shared folders -`Allow-group-directory-creation-role`. +Membership in one of the default groups also grants access to the corresponding shared folder (e.g., the `developer` +group has access to `~/shared/developer`). Since `2024.9.1`, Nebari requires a special role to access the shared folders: +`allow-group-directory-creation-role`. ::: ## In-depth Look at Default Roles and Groups -Groups represent a collection of users that perform similar actions and therefore -require similar permissions. By default, Nebari is deployed with the following groups: -`admin`, `developer`, and `analyst` (in roughly descending order of permissions and -scope). - -Roles on the other hand represent the type or category of user. This includes access and -permissions that this category of user will need to perform their regular job duties. -The differences between `groups` and `roles` are subtle. Particular roles (one or many), -like `conda_store_admin`, are associated with a particular group, such as `admin` and -any user in this group will then assume the role of `conda_store_admin`. - :::tip Technical Note -Roles in nebari can stack, meaning that if a user is in one group with role -`conda_store_admin` and another group with role `conda_store_viewer`, this user -ultimately has the role `conda_store_admin`. Also, in case of different level of access -originating from different groups, precedence is given to the highest level of access. +Roles in Nebari can stack, meaning that if a user is in one group with role `conda_store_admin` and another group with +role `conda_store_viewer`, this user ultimately has the role `conda_store_admin`. Also, in case of different level of +access originating from different groups, precedence is given to the highest level of access. ::: -Below is a table that outlines the default roles, groups, and permissions that come with -Nebari: +Below is a table that outlines the default roles, groups, and permissions that come with Nebari: import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; @@ -221,8 +123,8 @@ import { Table } from '@site/src/components/MarkdownTable';
- - +
+ -
+ -
+ -
- | - -:::info -Check -[Conda-store authorization model](https://conda-store.readthedocs.io/en/latest/contributing.html#authorization-model) -for more details on conda-store authorization. -::: +## Roles, Groups, and Scopes -:::caution -The role `jupyterhub_admin` gives users elevated permissions to JupyterHub and should be applied judiciously. As -mentioned in the table above, a JupyterHub admin is able to impersonate other users and view the contents of their home -folder. For more details, read through the -[JupyterHub documentation](https://z2jh.jupyter.org/en/stable/jupyterhub/customizing/user-management.html#admin-users). -:::note -See [Conda-Store Authorization -Model](https://conda-store.readthedocs.io/en/latest/contributing.html#authorization-model) -for additional details. -::: +When managing user access in Nebari, it's helpful to understand how **roles**, **groups**, and **scopes** work together: -:::warning -The `jupyterhub_admin` role grants permission to impersonate other users, including -viewing their home folder contents. Use it sparingly and refer to the [JupyterHub -docs](https://z2jh.jupyter.org/en/stable/jupyterhub/customizing/user-management.html#admin-users) -for more info. -::: +* **Roles**: A named set of permissions, while also providing a way to categorize users by their access level or job + function. Example: `conda_store_developer` or `jupyterhub_admin`. +* **Groups**: Collections of users who need similar permissions. For example, the `developer` group might contain + multiple roles like `conda_store_developer` and `jupyterhub_developer`. +* **Scopes**: A service's fine-grained permission entity within a role. For instance, a JupyterHub scope might be + `read:users:name`, while a Conda-Store scope might be `admin!namespace=analyst`. -## Roles, Groups, and Scopes - -As an example, we create a new group named `conda-store-manager`. This group will have administrator access to the -[Conda-Store service]. -When managing user access in Nebari, it's helpful to understand how **roles**, -**groups**, and **scopes** work together: - -- **Roles**: A named set of permissions, while also providing a way to categorize users - by their access level or job function. Example: `conda_store_developer` or - `jupyterhub_admin`. -- **Groups**: Collections of users who need similar permissions. For example, the - `developer` group might contain multiple roles like `conda_store_developer` and - `jupyterhub_developer`. -- **Scopes**: A Service's fine-grained permission entity within a role. For instance, a - JupyterHub scope might be `read:users:name` (to read other users’ names), while a - Conda-Store scope might be `admin!namespace=analyst` (to grant admin-level access in - the `analyst` namespace). - -Whenever you create or modify a role in Keycloak, it won’t affect anyone until it’s -assigned to a group or user. Below is the general process: +Whenever you create or modify a role in Keycloak, it won’t affect anyone until it’s assigned to a group or user. **Typical Workflow**: -1. **Create or edit** a role (and define its scopes) under the relevant **Client** - (e.g., `jupyterhub`, `conda_store`) in Keycloak. + +1. **Create or edit** a role (and define its scopes) under the relevant **Client** (e.g., `jupyterhub`, `conda_store`). 2. **Assign** this role to a **group** (or directly to an individual user). 3. **Add users** to that group if you haven’t already. -:::note -Users must log out and log back in for newly assigned roles to take effect. -::: - -### Granting Permissions to a User +### Granting Permissions to a User (via a Group) -Let’s say you have someone who only needs *administrator-level privileges for -Conda-Store*, while leaving other services at their default access. +Let’s say you have someone who only needs *administrator-level privileges for Conda-Store*, while leaving other services +at their default access. -As an example, we create a new group named `conda-store-manager`. This group will have -administrator access to the `Conda-Store` service. +As an example, we create a new group named `conda-store-manager`. This group will have administrator access to the +Conda-Store service. 1. Click **New** in the upper-right hand corner under **Groups**. ![Keycloak groups tab screenshot - user groups view](/img/how-tos/keycloak_groups.png) -- Then, give the new group an appropriate name. +* Then, give the new group an appropriate name. ![Keycloak add group form - name field set to conda-store-manager](/img/how-tos/keycloak_new_group1.png) @@ -424,92 +291,66 @@ administrator access to the `Conda-Store` service. In this example, the new group only has one mapped role, `conda_store_admin`; however, it's possible to attach multiple **Client Roles** to a single group. -2. Under **Role Mapping**, add the appropriate **Client Roles** as needed; there should - be no need to update the **Realm Roles**. - -![Keycloak group conda-store-manager form - role mappings tab focused with expanded client roles dropdown](/img/how-tos/keycloak_new_group2.png) -In this example, the new group only has one mapped role, `conda_store_admin`; however, -it's possible to attach multiple **Client Roles** to a single group. +![Keycloak group conda-store-manager form - role mappings tab focused](/img/how-tos/keycloak_new_group3.png) -![Keycloak group conda-store-manager form - role mappings tab focused ](/img/how-tos/keycloak_new_group3.png) - -Once complete, return to the **Users** section in the dashboard and add the relevant -users to this newly created group. - -:::note -To create new groups or modify (or delete) existing groups, log in as `root` and click -**Groups** on the left-hand side. -::: +Once complete, return to the **Users** section in the dashboard and add the relevant users to this newly created group. ### Creating Custom Roles with Scopes -Beyond Nebari’s default roles, you can also create your own with highly specific -permissions. The process for creating a role is similar, irrespective of the service. - -:::warning -Nebari currently only supports custom roles for the `jupyterhub` and `conda_store`. -Future releases may extend this to other services. -::: +Beyond Nebari’s default roles, you can also create your own with highly specific permissions. #### Components and Syntax -In Nebari, **scopes** are closely tied to the service (client) you’re working -with— In Keycloak's roles we identify them as “components.” +In Nebari, **scopes** are closely tied to the service (client) you’re working with—in Keycloak roles we identify them as +“components.” -- **Components**: - Specifies the service that a role’s scopes apply to (e.g., `jupyterhub` or `conda-store`). -- **Scopes**: - The actual permission strings recognized by that service. +* **Components**: Specifies the service that a role’s scopes apply to (e.g., `jupyterhub` or `conda-store`). +* **Scopes**: The actual permission strings recognized by that service.
#### JupyterHub Scopes -JupyterHub scopes syntax in Nebari follow [JupyterHub’s built-in RBAC -syntax](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html). For example: +JupyterHub scopes syntax in Nebari follow [JupyterHub’s built-in RBAC syntax](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html). For example: + ``` shares!user,read:users:name,read:groups:name ``` -- `shares!user`: Allows sharing servers with other users. -- `read:users:name`: Grants read access to other users’ names. -- `read:groups:name`: Grants read access to other groups’ names. -This combination allows a user to share their server with others (via `shares!user`) and -also read other users’ and groups’ names. +* `shares!user`: Allows sharing servers with other users. +* `read:users:name`: Grants read access to other users’ names. +* `read:groups:name`: Grants read access to other groups’ names. + +This combination allows a user to share their server with others (via `shares!user`) and also read other users’ and +groups’ names. -For a complete list of JupyterHub scopes, see the -[https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#available-scopes](JupyterHub -documentation). +For a complete list of JupyterHub scopes, see the JupyterHub documentation: +[https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#available-scopes](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#available-scopes) -Nebari extend these scoped including a new *share* scope that is reserved for -`Jhub-apps` usage, allowing, as seen above for sharing servers with other users, -including more specifcaly jhub-apps applications (apps). +Nebari extends these scopes for some integrations (e.g., `jhub-apps`) to support sharing apps with users/groups. --- #### Conda-Store Scopes Conda-Store scopes are defined at the namespace level. For example: + ``` admin!namespace=analyst,developer!namespace=nebari-git ``` -- `admin!namespace=analyst`: Grants an **admin** role in the `analyst` namespace. -- `developer!namespace=nebari-git`: Grants a **developer** role in the `nebari-git` namespace. -This grants an **admin** role in the `analyst` namespace, plus a **developer** role in -the `nebari-git` namespace. When assigned to a user or group, these permissions apply -only to those specified namespaces in Conda-Store. +* `admin!namespace=analyst`: Grants an **admin** role in the `analyst` namespace. +* `developer!namespace=nebari-git`: Grants a **developer** role in the `nebari-git` namespace. + +Conda-store scopes follow a simple syntax: -Conda-store scopes follow a much simpler syntax than JupyterHub scopes, as they are only -defined at the namespace level: ``` !namespace= ``` -whereas the `access_level` there entices the available conda-store' roles as per its own -documentation -[Conda-Store -role-mappings](https://conda.store/conda-store/explanations/conda-store-concepts#role-mappings). + +See: +[Conda-Store role mappings](https://conda.store/conda-store/explanations/conda-store-concepts#role-mappings)
@@ -520,77 +361,69 @@ login so users can work even if the Keycloak groups don’t define any `conda_st client roles. **What Nebari applies by default** -- **Personal namespace** → always **admin** + +* **Personal namespace** → always **admin** `"{username}/*" → {"admin"}` (cannot be downgraded by scopes). -- **Deployment default namespace** → **viewer** +* **Deployment default namespace** → **viewer** `"{default_namespace}/*" → {"viewer"}`. -- **Nebari default groups** (`analyst`, `developer`, `admin`) → **[handled internally](https://github.com/nebari-dev/nebari/blob/main/src/_nebari/stages/kubernetes_services/template/modules/kubernetes/services/conda-store/config/conda_store_config.py#L105)** +* **Nebari default groups** (`analyst`, `developer`, `admin`) → **handled internally** If a user is in one of these groups, Nebari binds a **same-named namespace** to the user with that group’s effective Conda-Store role (e.g., group `analyst` ⇒ `analyst/*`), **even when the Keycloak group itself has no client roles**. -- **SuperAdmin** (`conda_store_superadmin`) → full wildcard +* **SuperAdmin** (`conda_store_superadmin`) → full wildcard `"*/*" → {"admin"}` (includes delete & service-token privileges). **How it reconciles with Keycloak scopes** -- On each login, Nebari **removes non-default bindings** and re-applies permissions from - Keycloak role **scopes** (e.g., `admin!namespace=analyst`). -- If multiple grants target the same namespace, the **highest** permission wins. -- Order of precedence (highest → lowest): **SuperAdmin** → **explicit scopes** → **internal defaults**. -- Changes take effect after the user signs in again. -::: + +* On each login, Nebari re-applies permissions from Keycloak role **scopes** + (e.g., `admin!namespace=analyst`). +* If multiple grants target the same namespace, the **highest** permission wins. +* Order of precedence (highest → lowest): **SuperAdmin** → **explicit scopes** → **internal defaults**. +* Changes take effect after the user signs in again. + ::: ### Creating a Role -The process for creating a role is similar, irrespective of the service. To create a role for a service -In the following example, we create a new role for the `JupyterHub` client granting the -same level of permission outlined in the section above [JupyterHub Scopes](#jupyterhub-scopes). +In the following example, we create a new role for the `JupyterHub` client granting the same level of permission outlined +in the section above [JupyterHub Scopes](#jupyterhub-scopes). + +1. Select the appropriate client and click on **Add Role**. -1. Select the appropriate client and click on "Add Role". ![Keycloak client add jupyterhub role](/img/how-tos/keycloak_jupyterhub_client.png) -2. On the "Add Role" form, write a meaningful name and description for the role. Be sure to include what this role - intends to accomplish. Click "Save". +2. On the **Add Role** form, write a meaningful name and description for the role. Click **Save**. ![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_jupyterhub_add_role.png) -3. Now the role has been created, but it does nothing. Let's add some permissions to it by clicking on the "Attributes" - tab and adding scopes. The following sections will explain the `components` and `scopes` in more detail. +3. Now the role has been created, but it does nothing. Add permissions by clicking on the **Attributes** tab and adding + `components` and `scopes`. - ![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_add_role_attributes.png) +![Keycloak clients add jupyterhub role attributes](/img/how-tos/keycloak_add_role_attributes.png) ### Adding Role to Group(s) / User(s) -Creating a role in Keycloak has no effect on any user or group's permissions. To grant a set of permissions to a user or -group, we need to _attach_ the role to the user or group. To add a role to a user: +Creating a role in Keycloak has no effect on any user or group’s permissions until it is assigned. -1. Select users on the left sidebar and enter the username in the Lookup searchbar. +To add a role to a user: - ![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_select_user.png) +1. Select **Users** on the left sidebar and search for the user. -2. Select that user and click on the "Role Mappings" tab. +![Keycloak select user](/img/how-tos/keycloak_select_user.png) -![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_user_role_mapping_tab.png) +2. Select that user and click on the **Role Mappings** tab. -3. Select the Client associated with the Role being added. +![Keycloak user role mapping tab](/img/how-tos/keycloak_user_role_mapping_tab.png) -![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_user_role_mapping_roles.png) +3. Select the client associated with the role. -4. Select the role in the "Available Roles" and click on "Add Selected >>". +![Keycloak user role mapping roles](/img/how-tos/keycloak_user_role_mapping_roles.png) -![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_user_role_mapping_add_role.png) +4. Select the role in **Available Roles** and click **Add Selected >>**. -To attach a role to a group, follow the above steps by clicking on the groups tab and selecting a group instead of -selecting the user in the first step. +![Keycloak user role mapping add role](/img/how-tos/keycloak_user_role_mapping_add_role.png) -In the above section, we learned how to create a role with some attributes and attach it to a user or a group. Now we -will learn how to create scopes to grant a particular set of permissions to the user. - -:::note -After the roles are assigned to a user or group in Keycloak, the user **must** logout and login back in to the service -for the roles to take in effect. For example let's say we add a set of roles for `conda-store` to the user named "John -Doe", now for the user "John Doe" to be able to avail newly granted/revoked roles, they need to login to conda-store -again (similarly for `jupyterhub` as well), after the roles are granted/revoked. -::: +To attach a role to a group, follow the above steps but select the group under **Groups** and use the group’s **Role +Mappings** tab. ### Adding Users to a Group @@ -599,23 +432,23 @@ To add users to a group: 1. **Navigate to Users**: Click on **Users** in the left-hand menu. 2. **Select a User**: Choose the user you want to add to a group. 3. **Assign to Group**: - - Click on the **Groups** tab within the user's details. - - Click the **Join** button. - - Select the group (and subgroup, if applicable) you wish to add the user to. - - Click **Join** to add the user to the group. + + * Click on the **Groups** tab within the user's details. + * Click the **Join** button. + * Select the group (and subgroup, if applicable) you wish to add the user to. + * Click **Join** to add the user to the group. Repeat this process for all users who should be part of the group. ### Managing Subgroups Subgroups allow you to create a hierarchical structure of groups, representing organizational units, projects, or teams. -Subgroups inherit roles and attributes from their parent groups unless explicitly overridden. To manage subgroups: -- **Creating Subgroups**: Select the parent group, navigate to the **Sub Groups** tab, and create a new subgroup. -- **Assigning Roles to Subgroups**: Roles are not automatically inherited; assign roles to subgroups as needed. -- **Use Cases**: Organize groups hierarchically to reflect organizational structures. +* **Creating Subgroups**: Select the parent group, navigate to the **Sub Groups** tab, and create a new subgroup. +* **Assigning Roles to Subgroups**: Assign roles to subgroups as needed. +* **Use Cases**: Organize groups hierarchically to reflect organizational structures. For more information, see the [Keycloak documentation on Group Hierarchies](https://www.keycloak.org/docs/latest/server_admin/#group-hierarchies). @@ -654,13 +487,12 @@ An example directory structure: To enable directory creation and mounting for a group in JupyterHub, assign the `allow-group-directory-creation-role` to the group in Keycloak. -#### Steps to Assign the Role: - 1. **Navigate to the Group**: Log in to Keycloak as an administrator and select the group. 2. **Go to Role Mappings**: Click on the **Role Mappings** tab. 3. **Assign the Role**: - - Under **Client Roles**, select the `jupyterhub` client. - - Select `allow-group-directory-creation-role` and click **Add selected**. + + * Under **Client Roles**, select the `jupyterhub` client. + * Select `allow-group-directory-creation-role` and click **Add selected**. Users in this group will now have access to the group's shared directory in JupyterHub. @@ -670,8 +502,9 @@ To remove the group's directory access: 1. **Navigate to the Group's Role Mappings**: Select the group and go to the **Role Mappings** tab. 2. **Remove the Role**: - - Under **Assigned Roles**, select `allow-group-directory-creation-role`. - - Click **Remove selected**. + + * Under **Assigned Roles**, select `allow-group-directory-creation-role`. + * Click **Remove selected**. **Data Preservation:** No data is deleted when you remove the role; the directory is simply unmounted from users' JupyterLab sessions. @@ -681,196 +514,46 @@ JupyterLab sessions. Subgroups can have their own directories in JupyterHub if assigned the `allow-group-directory-creation-role`. Assign the role to subgroups as needed to control access and collaboration. -## In-Depth Look at Roles and Groups - -Understanding how roles and groups work together is essential for effectively managing access and permissions within -Nebari. - -### Groups - -Groups represent collections of users who perform similar functions or belong to the same organizational unit. They -simplify user management by allowing you to assign roles and permissions at the group level rather than individually to -each user. - -By default, Nebari is deployed with the following groups: - -- **admin**: Users with administrative privileges who can manage the system, configurations, and users. -- **developer**: Users who need access to development tools and environments. -- **analyst**: Users who primarily analyze data and may have restricted access compared to developers. - -Groups can be organized hierarchically using subgroups, allowing for more granular control and reflecting organizational -structures. - -:::info -**Shared Directories:** Users in a particular group will have access to that group's shared directory in JupyterHub if -the group has the `allow-group-directory-creation-role` assigned. -::: - -### Roles - -Roles define a set of permissions that grant access to specific resources or capabilities within Nebari's services. They -are assigned to groups or users and determine what actions they can perform. - -Roles can be of two types: - -- **Realm Roles**: Apply globally across all clients (applications) in Keycloak. -- **Client Roles**: Specific to a client (application), such as `jupyterhub`, `conda-store`, or `grafana`. - -Examples of roles include: - -- `admin`: Grants administrative privileges within a client. -- `developer`: Grants development-related permissions. -- `conda_store_admin`: Allows managing Conda environments in Conda-Store. - -### Interaction Between Roles and Groups - -Roles are assigned to groups, and users inherit those roles through their group memberships. This means that: - -- **Users in a Group Get the Group's Roles**: If a group has certain roles assigned, all users in that group will - inherit those roles. -- **Multiple Roles Can Be Assigned**: A group can have multiple roles from different clients, providing access to - various services. -- **Roles Are Not Inherited by Subgroups**: Roles need to be explicitly assigned to subgroups if you want them to have - specific permissions. - -### Role Hierarchies and Stacking - -Roles can be designed to reflect hierarchical permissions. For example, an `admin` role may encompass all the -permissions of a `developer` role. - -:::info -**Role Stacking:** If a user is a member of multiple groups with different roles, they effectively have the combined -permissions of all assigned roles. For instance, if a user is in a group with the `conda_store_admin` role and another -group with the `conda_store_viewer` role, the user will have administrative privileges in Conda-Store due to the higher -permission level of `conda_store_admin`. -::: - -### Assigning Roles to Groups - -When creating or editing a group in Keycloak: - -1. **Select the Group**: Navigate to the group you wish to assign roles to. -2. **Go to Role Mappings**: Click on the **Role Mappings** tab. -3. **Assign Roles**: - - Under **Client Roles**, select the client application. - - Select the roles you wish to assign and click **Add selected**. - -## Access Levels and Permissions - -Roles on the other hand represent the type or category of user. This includes access and permissions that this category -of user will need to perform their regular job duties. The differences between `groups` and `roles` are subtle. -Particular roles (one or many), like `conda_store_admin`, are associated with a particular group, such as `admin` and -any user in this group will then assume the role of `conda_store_admin`. - -:::info -These roles can be stacked. This means that if a user is in one group with role `conda_store_admin` and another group -with role `conda_store_viewer`, this user ultimately has the role `conda_store_admin`. -::: - -Below is a summary of default groups, their access to Nebari resources, assigned roles, and permissions descriptions. - -| Group | Access to Nebari Resources | Roles | Permissions Description | -| ------------ | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `analyst` |
  • Conda-Store
  • Jupyterhub
  • Argo Workflows
  • Grafana
|
  • `conda_store_developer`
  • `jupyterhub_developer`
  • `argo_viewer`
  • `grafana_viewer`
|
  • Default user permissions
  • Access to start a server instance and generate JupyterHub access token.
  • Read/write access to shared `analyst` folder group mount
  • Read access to `analyst` and write access to personal conda-store namespace
  • Read access to Argo-Workflows and Jupyter-Scheduler
  • Inherent Grafana permissions from [Grafana viewer scopes](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#organization-roles)
| -| `developer` |
  • Conda-Store
  • Dask
  • Jupyterhub
  • Argo Workflows
  • Grafana Developer
|
  • `conda_store_developer`
  • `dask_developer`
  • `jupyterhub_developer`
  • `argo_developer`
  • `grafana_developer`
|
  • All of the above access, plus...
  • Read access `developer` conda-store namespace
  • Access to create Dask clusters.
  • Read/write access to shared `developer` folder group mount
  • Read/create access to Argo-Workflows and Jupyter-Scheduler
  • Inherent Grafana permissions from [Grafana editor scopes](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#organization-roles)
| -| `admin` |
  • Conda-Store
  • Dask
  • Jupyterhub
  • Argo Workflows
  • Grafana
|
  • `conda_store_admin`
  • `dask_admin`
  • `jupyterhub_admin`
  • `argo_admin`
  • `grafana_admin`
|
  • All of the above access, plus...
  • Read/write access to all conda-store available namespaces/environments.
  • Access to Jupyterhub Admin page and can access JupyterLab users spaces
  • Access to Keycloak and can add remove users and groups
  • Inherent Grafana permissions from [Grafana administrator scopes](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#organization-roles)
| -| `superadmin` |
  • Conda-Store
  • Dask
  • Jupyterhub
  • Argo Workflows
  • Grafana
|
  • `conda_store_superadmin`
  • `dask_admin`
  • `jupyterhub_admin`
  • `argo_admin`
  • `realm_admin` (Keycloak)
  • `grafana_admin`
|
  • All of the above access, plus...
  • Delete (build and environment) access on conda-store
  • Full access to Keycloak (realm) (same as `root`)
| - -:::info -Check -[Conda-store authorization model](https://conda-store.readthedocs.io/en/latest/contributing.html#authorization-model) -for more details on conda-store authorization. -::: - -:::caution -The role `jupyterhub_admin` gives users elevated permissions to JupyterHub and should be applied judiciously. As -mentioned in the table above, a JupyterHub admin is able to impersonate other users and view the contents of their home -folder. For more details, read through the -[JupyterHub documentation](https://z2jh.jupyter.org/en/stable/jupyterhub/customizing/user-management.html#admin-users). -::: - -To create new groups or modify (or delete) existing groups, log in as `root` and click **Groups** on the left-hand side. - -As an example, we create a new group named `conda-store-manager`. This group will have administrator access to the -[Conda-Store service]. - -1. Click **New** in the upper-right hand corner under **Groups**. - -![Keycloak groups tab screenshot - user groups view](/img/how-tos/keycloak_groups.png) - -- Then, give the new group an appropriate name. - -![Keycloak add group form - name field set to conda-store-manager](/img/how-tos/keycloak_new_group1.png) - -2. Under **Role Mapping**, add the appropriate **Client Roles** as needed; there should be no need to update the **Realm - Roles**. - -![Keycloak group conda-store-manager form - role mappings tab focused with expanded client roles dropdown](/img/how-tos/keycloak_new_group2.png) - -In this example, the new group only has one mapped role, `conda_store_admin`; however, it's possible to attach multiple -**Client Roles** to a single group. - -![Keycloak group conda-store-manager form - role mappings tab focused ](/img/how-tos/keycloak_new_group3.png) - -Once complete, return to the **Users** section in the dashboard and add the relevant users to this newly created group. ## Components and Scopes When specifying fine-grained permissions in Keycloak, we use the `components` and `scopes` attributes. These attributes -are used to define the permissions that a role grants to a user or group. This model follows the RBAC (Role-Based Access -Control) model and is based upong the same structure utilized by -[Jupyterhub](https://jupyterhub.readthedocs.io/en/latest/rbac/index.html). +define the permissions that a role grants to a user or group. This model follows the RBAC (Role-Based Access Control) +model and is based upon the same structure utilized by +[JupyterHub RBAC](https://jupyterhub.readthedocs.io/en/latest/rbac/index.html). ### Components Attribute -We have seen in the above example the `component` attribute while creating a role. The value of this parameter depends -on the type of component in the service, we're creating a role for, currently we only have two components: +The value of the `component` attribute depends on the service we are creating a role for. Currently, Nebari supports: -- `jupyterhub`: to create `jupyterhub` native roles in the `jupyterhub` client. -- `conda-store`: to create `conda-store` roles in the `conda_store` client +* `jupyterhub`: to create `jupyterhub` native roles in the `jupyterhub` client. +* `conda-store`: to create `conda-store` roles in the `conda_store` client. ### JupyterHub Scopes The syntax for the `scopes` attribute for a `jupyterhub` role in Keycloak in Nebari follows the native RBAC scopes -syntax for JupyterHub itself. The documentation can be found -[here](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#scope-conventions). +syntax for JupyterHub itself. Documentation: +[https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#scope-conventions](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#scope-conventions) -As an example, scopes for allowing users to share apps in Nebari's `jhub-apps` launcher may look like this: +Example (sharing apps via `jhub-apps`): > `shares!user,read:users:name,read:groups:name` -The `scopes` defined above consists of three scopes: - -- `shares!user`: grants permissions to share user's server -- `read:users:name`: grants permissions to read other user's names -- `read:groups:name`: grants permissions to read other groups's names - -To be able to share a server to a group or a user you need to be able to read other user's or group's names and must -have permissions to be able to share your server, this is what this set of permissions implement. +* `shares!user`: grants permissions to share user's server +* `read:users:name`: grants permissions to read other user's names +* `read:groups:name`: grants permissions to read other groups's names ### Conda Store Scopes -The scopes for roles for the `conda-store` Client are applied to the `namespace` level of `conda-store`. +Conda-Store scopes are applied at the namespace level. -Below is example of granting a user specialized permissions to `conda-store`: +Example: > `admin!namespace=analyst,developer!namespace=nebari-git` -The `scopes` defined above consists of two scopes: - -- `admin!namespace=analyst`: grants `admin` access to namespace `analyst` -- `developer!namespace=nebari-git`: grants `developer` access to namespace `nebari-git` +* `admin!namespace=analyst`: grants `admin` access to namespace `analyst` +* `developer!namespace=nebari-git`: grants `developer` access to namespace `nebari-git` When attached to a user or a group, the above-mentioned permissions will be granted to the user/group. -1. On the "Add Role" form, write a meaningful name and description for the role. Be sure - to include what this role intends to accomplish. Click "Save". - -![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_jupyterhub_add_role.png) - -3. Now the role has been created, but it does nothing. Let's add some permissions to it - by clicking on the "Attributes" tab and adding scopes. The following sections will - explain the `components` and `scopes` in more detail. - - ![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_add_role_attributes.png) -To make these new permissions effective, assign the custom role to a user or group (via -**Role Mappings**). Again, any user changes require a re-login to become active. +To make these new permissions effective, assign the custom role to a user or group (via **Role Mappings**). Any changes +require a re-login to become active. From 726a57c7be0f5c9321410a5023cb9f0760ce8760 Mon Sep 17 00:00:00 2001 From: vinicius douglas cerutti Date: Sat, 24 Jan 2026 14:32:10 -0300 Subject: [PATCH 08/11] remove redundant section of docs --- .../docs/how-tos/fine-grained-permissions.mdx | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/docs/docs/how-tos/fine-grained-permissions.mdx b/docs/docs/how-tos/fine-grained-permissions.mdx index 5f2430331..8f39fffed 100644 --- a/docs/docs/how-tos/fine-grained-permissions.mdx +++ b/docs/docs/how-tos/fine-grained-permissions.mdx @@ -44,19 +44,6 @@ To create a new group in Keycloak: If you wish to organize your groups hierarchically, you can create subgroups by selecting a parent group and adding a subgroup. -## Fine-Grained Permissions via Keycloak - -Nebari uses [Keycloak](https://www.keycloak.org/) to centrally manage roles, permissions, and scopes for its core -services like `jupyterhub` and `conda-store`. By default, Nebari provides: - -- **Predefined roles** (e.g., `conda_store_admin`, `jupyterhub_developer`) -- **Default groups** (`admin`, `developer`, `analyst`, `superadmin`) -- **Clients** corresponding to key Nebari services - -These defaults cover most common access requirements. However, when more specific or granular access control is needed, -administrators can create custom roles and scopes in Keycloak—see -[Creating Custom Roles with Scopes](#creating-custom-roles-with-scopes). - ## Default Clients and Groups in Nebari Overview @@ -249,18 +236,23 @@ import { Table } from '@site/src/components/MarkdownTable'; -## Roles, Groups, and Scopes +## Roles, Groups, and Scopes in Nebari (via Keycloak) + +Nebari uses [Keycloak](https://www.keycloak.org/) to manage access for core services like `jupyterhub` and `conda-store`. +Access is expressed through three related concepts: -When managing user access in Nebari, it's helpful to understand how **roles**, **groups**, and **scopes** work together: +- **Roles**: Named permission bundles for a specific client/service (e.g., `conda_store_admin`, `jupyterhub_developer`). +- **Groups**: Collections of users who should receive the same roles (e.g., `admin`, `developer`, `analyst`). +- **Scopes**: Fine-grained permission strings attached to roles (e.g., `read:users:name` for JupyterHub, or + `admin!namespace=analyst` for Conda-Store). -* **Roles**: A named set of permissions, while also providing a way to categorize users by their access level or job - function. Example: `conda_store_developer` or `jupyterhub_admin`. -* **Groups**: Collections of users who need similar permissions. For example, the `developer` group might contain - multiple roles like `conda_store_developer` and `jupyterhub_developer`. -* **Scopes**: A service's fine-grained permission entity within a role. For instance, a JupyterHub scope might be - `read:users:name`, while a Conda-Store scope might be `admin!namespace=analyst`. +Nebari ships with **default clients, groups, and roles** that cover most needs. When you need more granular control, +create **custom roles with specific scopes** in Keycloak and assign them to a group (or directly to a user). -Whenever you create or modify a role in Keycloak, it won’t affect anyone until it’s assigned to a group or user. +:::note +Creating or editing a role does nothing until it is assigned via **Role Mappings** (to a group or user). Users must log +out and back in for changes to take effect. +::: **Typical Workflow**: From 1cc5875f3c9fab378217ec0a46dbb24dd303e4bb Mon Sep 17 00:00:00 2001 From: vinicius douglas cerutti Date: Tue, 27 Jan 2026 15:33:36 -0300 Subject: [PATCH 09/11] refactor docs to better sumirize contents --- .../docs/how-tos/fine-grained-permissions.mdx | 505 ++++++++++-------- 1 file changed, 290 insertions(+), 215 deletions(-) diff --git a/docs/docs/how-tos/fine-grained-permissions.mdx b/docs/docs/how-tos/fine-grained-permissions.mdx index 8f39fffed..b51390a52 100644 --- a/docs/docs/how-tos/fine-grained-permissions.mdx +++ b/docs/docs/how-tos/fine-grained-permissions.mdx @@ -4,31 +4,56 @@ title: Creating and Managing Groups, Roles, and Directories description: How to configure Keycloak's permissions, groups and roles, and manage group directories in JupyterHub. --- -Groups are a fundamental and vital part of the Nebari ecosystem. They are used to manage access to a wide range of -services within Nebari, including JupyterHub instances, Keycloak realms, Conda environments, and computing resources. By -grouping users based on roles, projects, or departments, Nebari simplifies the management of permissions and resource -sharing. +## TL;DR -Beyond managing access, groups play a crucial role in organizing and sharing data across the JupyterHub ecosystem. Each -group can have its own shared directory within JupyterHub, allowing users within the same group to collaborate -seamlessly by sharing files and resources. This facilitates team collaboration and ensures that data is organized and -accessible to those who need it. +- **Groups** organize users with similar permissions (e.g., `admin`, `developer`, `analyst`) +- **Roles** define permission bundles for specific services (e.g., `conda_store_admin`, `jupyterhub_developer`) +- **Scopes** are fine-grained permissions attached to roles (e.g., `read:users:name`) +- Create roles → Assign to groups → Add users to groups → Users must log out/in for changes to take effect +- Group directories in JupyterHub require the `allow-group-directory-creation-role` (since version 2024.9.1) + +## Overview + +Groups are a fundamental and vital part of the Nebari ecosystem. They are used to manage access to a wide range of services within Nebari, including JupyterHub instances, Keycloak realms, Conda environments, and computing resources. By grouping users based on roles, projects, or departments, Nebari simplifies the management of permissions and resource sharing. + +Beyond managing access, groups play a crucial role in organizing and sharing data across the JupyterHub ecosystem. Each group can have its own shared directory within JupyterHub, allowing users within the same group to collaborate seamlessly by sharing files and resources. This facilitates team collaboration and ensures that data is organized and accessible to those who need it. In this document, we will cover: -- How to create and manage groups and subgroups in Keycloak -- How to assign roles and permissions to groups -- How groups and roles interact within Nebari's services, such as JupyterHub and Conda-Store -- How to manage group directories in JupyterHub +- [Understanding Roles, Groups, and Scopes](#understanding-roles-groups-and-scopes) +- [Managing Groups in Keycloak](#managing-groups-in-keycloak) +- [Default Clients, Groups, and Permissions](#default-clients-groups-and-permissions) +- [Creating Custom Roles with Scopes](#creating-custom-roles-with-scopes) +- [Managing Users and Group Membership](#managing-users-and-group-membership) +- [Managing Group Directories in JupyterHub](#managing-group-directories-in-jupyterhub) +- [Permission Precedence and Troubleshooting](#permission-precedence-and-troubleshooting) + +## Understanding Roles, Groups, and Scopes + +Nebari uses [Keycloak](https://www.keycloak.org/) to manage access for core services like `jupyterhub` and `conda-store`. Access is expressed through three related concepts: + +- **Roles**: Named permission bundles for a specific client/service (e.g., `conda_store_admin`, `jupyterhub_developer`). Roles define what actions can be performed. +- **Groups**: Collections of users who should receive the same roles (e.g., `admin`, `developer`, `analyst`). Groups aggregate users with similar responsibilities. +- **Scopes**: Fine-grained permission strings attached to roles (e.g., `read:users:name` for JupyterHub, or `admin!namespace=analyst` for Conda-Store). Scopes are the actual permissions. +- **Components**: Specifies the service that a role's scopes apply to (e.g., `jupyterhub` or `conda-store`). + +**Permission Flow**: User → Group → Role → Scopes → Permissions + +Nebari ships with **default clients, groups, and roles** that cover most needs. When you need more granular control, create **custom roles with specific scopes** in Keycloak and assign them to a group (or directly to a user). + +:::note +Creating or editing a role does nothing until it is assigned via **Role Mappings** (to a group or user). Users must log out and back in for changes to take effect because Keycloak issues new access tokens on login. +::: + +:::tip Technical Note +Roles in Nebari can stack, meaning that if a user is in one group with role `conda_store_admin` and another group with role `conda_store_viewer`, this user ultimately has the role `conda_store_admin`. In case of different levels of access originating from different groups, precedence is given to the highest level of access. See [Permission Precedence](#permission-precedence-and-troubleshooting) for details. +::: ## Managing Groups in Keycloak -Keycloak is the identity and access management service used in Nebari. It allows you to create and manage users, groups, -roles, and permissions. Groups in Keycloak are collections of users, and they can be assigned specific roles that grant -permissions to access various services within Nebari. +Keycloak is the identity and access management service used in Nebari. It allows you to create and manage users, groups, roles, and permissions. Groups in Keycloak are collections of users, and they can be assigned specific roles that grant permissions to access various services within Nebari. -For detailed information on managing groups in Keycloak, refer to the -[Keycloak documentation on Group Management](https://www.keycloak.org/docs/latest/server_admin/#_group_management). +For detailed information on managing groups in Keycloak, refer to the [Keycloak documentation on Group Management](https://www.keycloak.org/docs/latest/server_admin/#_group_management). Below we outline the steps specific to Nebari. @@ -38,29 +63,53 @@ To create a new group in Keycloak: 1. **Log in to Keycloak** as an administrator (usually the `root` user). 2. **Navigate to Groups**: Click on **Groups** in the left-hand menu. -3. **Create the Group**: Click the **New** button, enter an appropriate name for your new group (e.g., - `conda-store-manager`), and save. +3. **Create the Group**: Click the **New** button, enter an appropriate name for your new group (e.g., `conda-store-manager`), and save. + +If you wish to organize your groups hierarchically, you can create subgroups by selecting a parent group and adding a subgroup. + +**Example Workflow**: Creating a `conda-store-manager` group + +1. Click **New** in the upper-right corner under **Groups**. + +![Keycloak groups tab screenshot - user groups view](/img/how-tos/keycloak_groups.png) + +2. Give the new group an appropriate name. + +![Keycloak add group form - name field set to conda-store-manager](/img/how-tos/keycloak_new_group1.png) + +3. Under **Role Mapping**, add the appropriate **Client Roles** as needed; there should be no need to update the **Realm Roles**. + +![Keycloak group conda-store-manager form - role mappings tab focused with expanded client roles dropdown](/img/how-tos/keycloak_new_group2.png) + +In this example, the new group only has one mapped role, `conda_store_admin`; however, it's possible to attach multiple **Client Roles** to a single group. + +![Keycloak group conda-store-manager form - role mappings tab focused](/img/how-tos/keycloak_new_group3.png) -If you wish to organize your groups hierarchically, you can create subgroups by selecting a parent group and adding a -subgroup. +### Managing Subgroups + +Subgroups allow you to create a hierarchical structure of groups, representing organizational units, projects, or teams. -## Default Clients and Groups in Nebari +To manage subgroups: -Overview +- **Creating Subgroups**: Select the parent group, navigate to the **Sub Groups** tab, and create a new subgroup. +- **Assigning Roles to Subgroups**: Assign roles to subgroups following the same process as regular groups. +- **Use Cases**: Organize groups hierarchically to reflect organizational structures (e.g., `engineering` → `data-science`, `engineering` → `backend`). -- **Roles**: Define a set of permissions (or scopes) for a particular client (service). -- **Groups**: Aggregate users who share similar permissions. -- **Scopes**: The individual permissions (e.g., `read:users:name`) that can be attached to a role. +For more information, see the [Keycloak documentation on Group Hierarchies](https://www.keycloak.org/docs/latest/server_admin/#group-hierarchies). -By creating custom roles with specific scopes, and assigning them to groups or users, you can fine-tune exactly who can -do what in your Nebari environment. More details on how to create roles and scopes can be found in the -[Creating a Role](#creating-a-role) section. +### Deleting a Group -## Default Clients and permissions in Nebari +To delete a group: -A fresh Nebari deployment comes with several custom Keycloak clients (services) enabled. You can manage these from -**Clients** in the Keycloak admin console: +1. **Navigate to Groups**: Click on **Groups** in the left-hand menu. +2. **Select the Group**: Click on the group you want to delete. +3. **Delete**: Click the **Delete** button and confirm. +**Important**: Deleting a group does not delete the users in that group. Users will simply lose the permissions associated with that group. Any shared directories associated with the group will remain in the file system but will no longer be mounted for users. + +## Default Clients, Groups, and Permissions + +A fresh Nebari deployment comes with several custom Keycloak clients (services) enabled. You can manage these from **Clients** in the Keycloak admin console: ```yaml clients: - jupyterhub @@ -68,37 +117,25 @@ clients: - grafana (if monitoring is enabled) - argo-server-sso (if argo is enabled) - forwardauth -```` +``` -To manage and configure these clients, you can navigate to the `Clients` tab within the Keycloak admin console, as -illustrated in the image below. +To manage and configure these clients, you can navigate to the `Clients` tab within the Keycloak admin console, as illustrated in the image below. ![Keycloak clients](/img/how-tos/fine_grainer_permissions_keycloak_clients.png) This can be accessed at `/auth/admin/master/console/#/realms/nebari/clients` -Groups represent a collection of users that perform similar actions and therefore require similar permissions. By -default, Nebari is deployed with the following groups: `admin`, `developer`, and `analyst` (in roughly descending order -of permissions and scope). +### Default Groups and Their Roles -Roles on the other hand represent the type or category of user. This includes access and permissions that this category -of user will need to perform their regular job duties. The differences between `groups` and `roles` are subtle. -Particular roles (one or many), like `conda_store_admin`, are associated with a particular group, such as `admin` and -any user in this group will then assume the role of `conda_store_admin`. +Groups represent a collection of users that perform similar actions and therefore require similar permissions. By default, Nebari is deployed with the following groups: `admin`, `developer`, and `analyst` (in roughly descending order of permissions and scope). + +Roles on the other hand represent the type or category of user. This includes access and permissions that this category of user will need to perform their regular job duties. The differences between `groups` and `roles` are subtle. Particular roles (one or many), like `conda_store_admin`, are associated with a particular group, such as `admin` and any user in this group will then assume the role of `conda_store_admin`. :::info Shared Folders -Membership in one of the default groups also grants access to the corresponding shared folder (e.g., the `developer` -group has access to `~/shared/developer`). Since `2024.9.1`, Nebari requires a special role to access the shared folders: -`allow-group-directory-creation-role`. +Membership in one of the default groups also grants access to the corresponding shared folder (e.g., the `developer` group has access to `~/shared/developer`). Since `2024.9.1`, Nebari requires a special role to access the shared folders: `allow-group-directory-creation-role`. ::: -## In-depth Look at Default Roles and Groups - -:::tip Technical Note -Roles in Nebari can stack, meaning that if a user is in one group with role `conda_store_admin` and another group with -role `conda_store_viewer`, this user ultimately has the role `conda_store_admin`. Also, in case of different level of -access originating from different groups, precedence is given to the highest level of access. -::: +### Default Roles and Permissions Below is a table that outlines the default roles, groups, and permissions that come with Nebari: @@ -236,148 +273,114 @@ import { Table } from '@site/src/components/MarkdownTable'; -## Roles, Groups, and Scopes in Nebari (via Keycloak) - -Nebari uses [Keycloak](https://www.keycloak.org/) to manage access for core services like `jupyterhub` and `conda-store`. -Access is expressed through three related concepts: - -- **Roles**: Named permission bundles for a specific client/service (e.g., `conda_store_admin`, `jupyterhub_developer`). -- **Groups**: Collections of users who should receive the same roles (e.g., `admin`, `developer`, `analyst`). -- **Scopes**: Fine-grained permission strings attached to roles (e.g., `read:users:name` for JupyterHub, or - `admin!namespace=analyst` for Conda-Store). - -Nebari ships with **default clients, groups, and roles** that cover most needs. When you need more granular control, -create **custom roles with specific scopes** in Keycloak and assign them to a group (or directly to a user). - -:::note -Creating or editing a role does nothing until it is assigned via **Role Mappings** (to a group or user). Users must log -out and back in for changes to take effect. -::: - -**Typical Workflow**: - -1. **Create or edit** a role (and define its scopes) under the relevant **Client** (e.g., `jupyterhub`, `conda_store`). -2. **Assign** this role to a **group** (or directly to an individual user). -3. **Add users** to that group if you haven’t already. - -### Granting Permissions to a User (via a Group) - -Let’s say you have someone who only needs *administrator-level privileges for Conda-Store*, while leaving other services -at their default access. - -As an example, we create a new group named `conda-store-manager`. This group will have administrator access to the -Conda-Store service. - -1. Click **New** in the upper-right hand corner under **Groups**. - -![Keycloak groups tab screenshot - user groups view](/img/how-tos/keycloak_groups.png) - -* Then, give the new group an appropriate name. +## Creating Custom Roles with Scopes -![Keycloak add group form - name field set to conda-store-manager](/img/how-tos/keycloak_new_group1.png) - -2. Under **Role Mapping**, add the appropriate **Client Roles** as needed; there should be no need to update the **Realm - Roles**. - -![Keycloak group conda-store-manager form - role mappings tab focused with expanded client roles dropdown](/img/how-tos/keycloak_new_group2.png) - -In this example, the new group only has one mapped role, `conda_store_admin`; however, it's possible to attach multiple -**Client Roles** to a single group. - -![Keycloak group conda-store-manager form - role mappings tab focused](/img/how-tos/keycloak_new_group3.png) +Beyond Nebari's default roles, you can also create your own with highly specific permissions. -Once complete, return to the **Users** section in the dashboard and add the relevant users to this newly created group. +### Understanding Components and Scopes -### Creating Custom Roles with Scopes +In Nebari, **scopes** are closely tied to the service (client) you're working with—in Keycloak roles we identify them as "components." -Beyond Nebari’s default roles, you can also create your own with highly specific permissions. +#### Components Attribute -#### Components and Syntax +The value of the `component` attribute depends on the service we are creating a role for. Currently, Nebari supports: -In Nebari, **scopes** are closely tied to the service (client) you’re working with—in Keycloak roles we identify them as -“components.” +- `jupyterhub`: to create `jupyterhub` native roles in the `jupyterhub` client. +- `conda-store`: to create `conda-store` roles in the `conda_store` client. -* **Components**: Specifies the service that a role’s scopes apply to (e.g., `jupyterhub` or `conda-store`). -* **Scopes**: The actual permission strings recognized by that service. +#### Scope Syntax by Service
-#### JupyterHub Scopes +##### JupyterHub Scopes + +JupyterHub scopes syntax in Nebari follow [JupyterHub's built-in RBAC syntax](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html). -JupyterHub scopes syntax in Nebari follow [JupyterHub’s built-in RBAC syntax](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html). For example: +**Syntax**: `scope1,scope2,scope3` +**Example**: ``` shares!user,read:users:name,read:groups:name ``` -* `shares!user`: Allows sharing servers with other users. -* `read:users:name`: Grants read access to other users’ names. -* `read:groups:name`: Grants read access to other groups’ names. +- `shares!user`: Allows sharing servers with other users. +- `read:users:name`: Grants read access to other users' names. +- `read:groups:name`: Grants read access to other groups' names. -This combination allows a user to share their server with others (via `shares!user`) and also read other users’ and -groups’ names. +This combination allows a user to share their server with others (via `shares!user`) and also read other users' and groups' names. -For a complete list of JupyterHub scopes, see the JupyterHub documentation: -[https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#available-scopes](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#available-scopes) +**Common Use Case - App Sharing**: +``` +shares!user=alice,shares!group=data-science,read:users:name,read:groups:name +``` +Allows sharing apps with user `alice` and the `data-science` group. + +For a complete list of JupyterHub scopes, see the JupyterHub documentation: [https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#available-scopes](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#available-scopes) Nebari extends these scopes for some integrations (e.g., `jhub-apps`) to support sharing apps with users/groups. --- -#### Conda-Store Scopes +##### Conda-Store Scopes + +Conda-Store scopes are defined at the namespace level. + +**Syntax**: `!namespace=` -Conda-Store scopes are defined at the namespace level. For example: +**Access Levels**: `admin`, `developer`, `viewer` +**Example**: ``` admin!namespace=analyst,developer!namespace=nebari-git ``` -* `admin!namespace=analyst`: Grants an **admin** role in the `analyst` namespace. -* `developer!namespace=nebari-git`: Grants a **developer** role in the `nebari-git` namespace. - -Conda-store scopes follow a simple syntax: +- `admin!namespace=analyst`: Grants an **admin** role in the `analyst` namespace. +- `developer!namespace=nebari-git`: Grants a **developer** role in the `nebari-git` namespace. +**Common Use Case - Multi-namespace Access**: ``` -!namespace= +admin!namespace=data-science,viewer!namespace=production,viewer!namespace=shared-datasets ``` +Grants admin access to the team's namespace, but read-only to production and shared datasets. -See: -[Conda-Store role mappings](https://conda.store/conda-store/explanations/conda-store-concepts#role-mappings) +See: [Conda-Store role mappings](https://conda.store/conda-store/explanations/conda-store-concepts#role-mappings)
-:::note Conda-Store defaults and Nebari’s internal group handling -Nebari grants some Conda-Store permissions **automatically**, regardless of how your -Keycloak groups are configured. The authentication class applies these defaults at -login so users can work even if the Keycloak groups don’t define any `conda_store_*` -client roles. +:::note Conda-Store Defaults and Nebari's Internal Group Handling +Nebari grants some Conda-Store permissions **automatically**, regardless of how your Keycloak groups are configured. The authentication class applies these defaults at login so users can work even if the Keycloak groups don't define any `conda_store_*` client roles. **What Nebari applies by default** -* **Personal namespace** → always **admin** +- **Personal namespace** → always **admin** `"{username}/*" → {"admin"}` (cannot be downgraded by scopes). -* **Deployment default namespace** → **viewer** +- **Deployment default namespace** → **viewer** `"{default_namespace}/*" → {"viewer"}`. -* **Nebari default groups** (`analyst`, `developer`, `admin`) → **handled internally** - If a user is in one of these groups, Nebari binds a **same-named namespace** to the - user with that group’s effective Conda-Store role (e.g., group `analyst` ⇒ `analyst/*`), - **even when the Keycloak group itself has no client roles**. -* **SuperAdmin** (`conda_store_superadmin`) → full wildcard +- **Nebari default groups** (`analyst`, `developer`, `admin`) → **handled internally** + If a user is in one of these groups, Nebari binds a **same-named namespace** to the user with that group's effective Conda-Store role (e.g., group `analyst` ⇒ `analyst/*`), **even when the Keycloak group itself has no client roles**. +- **SuperAdmin** (`conda_store_superadmin`) → full wildcard `"*/*" → {"admin"}` (includes delete & service-token privileges). **How it reconciles with Keycloak scopes** -* On each login, Nebari re-applies permissions from Keycloak role **scopes** - (e.g., `admin!namespace=analyst`). -* If multiple grants target the same namespace, the **highest** permission wins. -* Order of precedence (highest → lowest): **SuperAdmin** → **explicit scopes** → **internal defaults**. -* Changes take effect after the user signs in again. - ::: +- On each login, Nebari re-applies permissions from Keycloak role **scopes** (e.g., `admin!namespace=analyst`). +- If multiple grants target the same namespace, the **highest** permission wins. +- Order of precedence (highest → lowest): **SuperAdmin** → **explicit scopes** → **internal defaults**. +- Changes take effect after the user signs in again. +::: + +### Step-by-Step: Creating a Custom Role + +**Typical Workflow**: + +1. **Create** a role (and define its scopes) under the relevant **Client** (e.g., `jupyterhub`, `conda_store`). +2. **Assign** this role to a **group** (or directly to an individual user). +3. **Add users** to that group if you haven't already. +4. **Users log out and back in** for changes to take effect. -### Creating a Role +#### Step 1: Create the Role -In the following example, we create a new role for the `JupyterHub` client granting the same level of permission outlined -in the section above [JupyterHub Scopes](#jupyterhub-scopes). +In the following example, we create a new role for the `JupyterHub` client granting permissions to share apps with users and groups. 1. Select the appropriate client and click on **Add Role**. @@ -387,16 +390,26 @@ in the section above [JupyterHub Scopes](#jupyterhub-scopes). ![Keycloak clients add jupyterhub role form](/img/how-tos/keycloak_jupyterhub_add_role.png) -3. Now the role has been created, but it does nothing. Add permissions by clicking on the **Attributes** tab and adding - `components` and `scopes`. +3. Now the role has been created, but it does nothing. Add permissions by clicking on the **Attributes** tab and adding `components` and `scopes`. ![Keycloak clients add jupyterhub role attributes](/img/how-tos/keycloak_add_role_attributes.png) -### Adding Role to Group(s) / User(s) +#### Step 2: Assign Role to a Group -Creating a role in Keycloak has no effect on any user or group’s permissions until it is assigned. +Creating a role in Keycloak has no effect on any user or group's permissions until it is assigned. -To add a role to a user: +To add a role to a group: + +1. Select **Groups** on the left sidebar and search for the group. +2. Select that group and click on the **Role Mappings** tab. +3. Select the client associated with the role from the **Client Roles** dropdown. +4. Select the role in **Available Roles** and click **Add Selected >>**. + +Once complete, any users in this group will inherit the role's permissions after they log out and back in. + +#### Step 3: Assign Role to a User (Alternative) + +To add a role directly to a user: 1. Select **Users** on the left sidebar and search for the user. @@ -414,8 +427,24 @@ To add a role to a user: ![Keycloak user role mapping add role](/img/how-tos/keycloak_user_role_mapping_add_role.png) -To attach a role to a group, follow the above steps but select the group under **Groups** and use the group’s **Role -Mappings** tab. +### Example: Custom Role Scenarios + +**Scenario 1: Data Science Team Lead** +- **Goal**: Admin access to team namespace, read-only to production +- **Component**: `conda-store` +- **Scopes**: `admin!namespace=data-science,viewer!namespace=production` + +**Scenario 2: App Collaborator** +- **Goal**: Share JupyterHub apps with specific users and groups +- **Component**: `jupyterhub` +- **Scopes**: `shares!user,shares!group,read:users:name,read:groups:name` + +**Scenario 3: Cross-functional Analyst** +- **Goal**: Developer access to multiple namespaces +- **Component**: `conda-store` +- **Scopes**: `developer!namespace=marketing,developer!namespace=sales,viewer!namespace=finance` + +## Managing Users and Group Membership ### Adding Users to a Group @@ -424,43 +453,47 @@ To add users to a group: 1. **Navigate to Users**: Click on **Users** in the left-hand menu. 2. **Select a User**: Choose the user you want to add to a group. 3. **Assign to Group**: - - * Click on the **Groups** tab within the user's details. - * Click the **Join** button. - * Select the group (and subgroup, if applicable) you wish to add the user to. - * Click **Join** to add the user to the group. + - Click on the **Groups** tab within the user's details. + - Click the **Join** button. + - Select the group (and subgroup, if applicable) you wish to add the user to. + - Click **Join** to add the user to the group. Repeat this process for all users who should be part of the group. -### Managing Subgroups +### Removing Users from a Group -Subgroups allow you to create a hierarchical structure of groups, representing organizational units, projects, or teams. +To remove users from a group: -To manage subgroups: +1. **Navigate to Users**: Click on **Users** in the left-hand menu. +2. **Select a User**: Choose the user you want to remove from a group. +3. **Remove from Group**: + - Click on the **Groups** tab within the user's details. + - Find the group in the **Group Membership** list. + - Click **Leave** next to the group name. -* **Creating Subgroups**: Select the parent group, navigate to the **Sub Groups** tab, and create a new subgroup. -* **Assigning Roles to Subgroups**: Assign roles to subgroups as needed. -* **Use Cases**: Organize groups hierarchically to reflect organizational structures. +The user will immediately lose access to group-level permissions, but changes won't take effect until their next login (when a new access token is issued). -For more information, see the -[Keycloak documentation on Group Hierarchies](https://www.keycloak.org/docs/latest/server_admin/#group-hierarchies). +### Removing Roles from Users or Groups + +To remove a role from a user or group: + +1. Navigate to the user or group's **Role Mappings** tab. +2. Under **Assigned Roles**, find the role you want to remove. +3. Select the role and click **Remove selected**. + +Again, changes take effect after the user logs out and back in. ## Managing Group Directories in JupyterHub :::note Important -As of version `2024.9.1`, JupyterHub creates and mounts directories only for groups with the -`allow-group-directory-creation-role`. By default, this includes the `admin`, `analyst`, and `developer` groups. -Previously, directories were automatically created for all Keycloak groups. This change gives administrators more -control over shared directories and overall access management without cluttering the file system. +As of version `2024.9.1`, JupyterHub creates and mounts directories only for groups with the `allow-group-directory-creation-role`. By default, this includes the `admin`, `analyst`, and `developer` groups. Previously, directories were automatically created for all Keycloak groups. This change gives administrators more control over shared directories and overall access management without cluttering the file system. ::: ### Understanding Group Directories -A group directory in JupyterHub is a shared folder accessible to all members of a specific group. It provides a shared -space within the file system for collaboration. +A group directory in JupyterHub is a shared folder accessible to all members of a specific group. It provides a shared space within the file system for collaboration. An example directory structure: - ```yaml /shared ├── admin @@ -476,76 +509,118 @@ An example directory structure: ### Assigning the `allow-group-directory-creation-role` to a Group -To enable directory creation and mounting for a group in JupyterHub, assign the `allow-group-directory-creation-role` to -the group in Keycloak. +To enable directory creation and mounting for a group in JupyterHub, assign the `allow-group-directory-creation-role` to the group in Keycloak. 1. **Navigate to the Group**: Log in to Keycloak as an administrator and select the group. 2. **Go to Role Mappings**: Click on the **Role Mappings** tab. 3. **Assign the Role**: + - Under **Client Roles**, select the `jupyterhub` client. + - Select `allow-group-directory-creation-role` and click **Add selected**. - * Under **Client Roles**, select the `jupyterhub` client. - * Select `allow-group-directory-creation-role` and click **Add selected**. +Users in this group will now have access to the group's shared directory in JupyterHub after they log out and back in. -Users in this group will now have access to the group's shared directory in JupyterHub. - -### Rolling Back the Change +### Removing Group Directory Access To remove the group's directory access: 1. **Navigate to the Group's Role Mappings**: Select the group and go to the **Role Mappings** tab. 2. **Remove the Role**: + - Under **Assigned Roles**, select `allow-group-directory-creation-role`. + - Click **Remove selected**. - * Under **Assigned Roles**, select `allow-group-directory-creation-role`. - * Click **Remove selected**. - -**Data Preservation:** No data is deleted when you remove the role; the directory is simply unmounted from users' -JupyterLab sessions. +**Data Preservation**: No data is deleted when you remove the role; the directory is simply unmounted from users' JupyterLab sessions. ### Managing Subgroup Directories -Subgroups can have their own directories in JupyterHub if assigned the `allow-group-directory-creation-role`. Assign the -role to subgroups as needed to control access and collaboration. +Subgroups can have their own directories in JupyterHub if assigned the `allow-group-directory-creation-role`. Assign the role to subgroups as needed to control access and collaboration. + +**Example Hierarchy**: +```yaml +/shared +├── engineering +│ ├── data-science +│ │ └── models +│ └── backend +│ └── configs +└── marketing + └── campaigns +``` -## Components and Scopes +## Permission Precedence and Troubleshooting -When specifying fine-grained permissions in Keycloak, we use the `components` and `scopes` attributes. These attributes -define the permissions that a role grants to a user or group. This model follows the RBAC (Role-Based Access Control) -model and is based upon the same structure utilized by -[JupyterHub RBAC](https://jupyterhub.readthedocs.io/en/latest/rbac/index.html). +### How Permissions Stack -### Components Attribute +When a user belongs to multiple groups or has multiple roles assigned, Nebari uses the following rules to determine effective permissions: -The value of the `component` attribute depends on the service we are creating a role for. Currently, Nebari supports: +1. **Highest Permission Wins**: If conflicting permissions exist, the most permissive one takes precedence. + - Example: User in both `viewer` group (read-only) and `admin` group (read-write) → user gets read-write access + +2. **Service-Specific Precedence**: + - **Conda-Store**: SuperAdmin > Explicit Scopes > Internal Defaults > No Access + - **JupyterHub**: Scopes are additive; all granted scopes apply + +3. **Personal Namespaces**: Always admin access in Conda-Store (cannot be downgraded) + +### Common Issues and Solutions -* `jupyterhub`: to create `jupyterhub` native roles in the `jupyterhub` client. -* `conda-store`: to create `conda-store` roles in the `conda_store` client. +#### Issue 1: Changes Not Taking Effect -### JupyterHub Scopes +**Symptom**: User was added to a group or assigned a role, but still can't access resources. -The syntax for the `scopes` attribute for a `jupyterhub` role in Keycloak in Nebari follows the native RBAC scopes -syntax for JupyterHub itself. Documentation: -[https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#scope-conventions](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#scope-conventions) +**Solution**: +- User must **log out completely** and **log back in** +- Keycloak issues new access tokens on login with updated permissions +- Refreshing the page is not sufficient -Example (sharing apps via `jhub-apps`): +#### Issue 2: User Has Too Many Permissions -> `shares!user,read:users:name,read:groups:name` +**Symptom**: User can access resources they shouldn't be able to. -* `shares!user`: grants permissions to share user's server -* `read:users:name`: grants permissions to read other user's names -* `read:groups:name`: grants permissions to read other groups's names +**Solution**: +1. Check all groups the user belongs to +2. Check direct role assignments to the user +3. Remember: highest permission wins, so remove the more permissive role/group +4. User must log out and back in -### Conda Store Scopes +#### Issue 3: Group Directory Not Appearing -Conda-Store scopes are applied at the namespace level. +**Symptom**: Users in a group can't see the shared directory. -Example: +**Solution**: +1. Verify the group has `allow-group-directory-creation-role` assigned +2. User must log out and back in to mount the directory +3. Check JupyterHub logs for errors +4. Verify the directory exists in the file system + +#### Issue 4: Custom Role Not Working + +**Symptom**: Created a custom role with scopes, but it has no effect. + +**Solution**: +1. Verify `components` attribute is set correctly (e.g., `jupyterhub` or `conda-store`) +2. Verify `scopes` attribute syntax matches the service requirements +3. Ensure role is assigned to a group or user via **Role Mappings** +4. User must log out and back in + +### Testing Permissions + +To verify a user's permissions: + +1. **JupyterHub**: Check the user's token scopes via the JupyterHub API: +```bash + curl -H "Authorization: token " \ + https:///hub/api/user +``` -> `admin!namespace=analyst,developer!namespace=nebari-git` +2. **Conda-Store**: Attempt to access namespaces through the UI or API and observe access levels -* `admin!namespace=analyst`: grants `admin` access to namespace `analyst` -* `developer!namespace=nebari-git`: grants `developer` access to namespace `nebari-git` +3. **Keycloak**: Review the user's **Role Mappings** and **Group Membership** tabs -When attached to a user or a group, the above-mentioned permissions will be granted to the user/group. +## See Also -To make these new permissions effective, assign the custom role to a user or group (via **Role Mappings**). Any changes -require a re-login to become active. +- [Keycloak Official Documentation - Group Management](https://www.keycloak.org/docs/latest/server_admin/#_group_management) +- [Keycloak Official Documentation - Group Hierarchies](https://www.keycloak.org/docs/latest/server_admin/#group-hierarchies) +- [JupyterHub RBAC Documentation](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html) +- [Conda-Store Role Mappings](https://conda.store/conda-store/explanations/conda-store-concepts#role-mappings) +- [Nebari Authentication and Authorization](../explanations/authentication-authorization.md) +- [Managing Users in Nebari](./user-management.md) From fbb7bf313cb07b8dd2e576f179ead98fed5431f4 Mon Sep 17 00:00:00 2001 From: vinicius douglas cerutti Date: Tue, 27 Jan 2026 15:40:36 -0300 Subject: [PATCH 10/11] fix broken links --- docs/docs/how-tos/fine-grained-permissions.mdx | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/docs/how-tos/fine-grained-permissions.mdx b/docs/docs/how-tos/fine-grained-permissions.mdx index b51390a52..5eb436c19 100644 --- a/docs/docs/how-tos/fine-grained-permissions.mdx +++ b/docs/docs/how-tos/fine-grained-permissions.mdx @@ -622,5 +622,3 @@ To verify a user's permissions: - [Keycloak Official Documentation - Group Hierarchies](https://www.keycloak.org/docs/latest/server_admin/#group-hierarchies) - [JupyterHub RBAC Documentation](https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html) - [Conda-Store Role Mappings](https://conda.store/conda-store/explanations/conda-store-concepts#role-mappings) -- [Nebari Authentication and Authorization](../explanations/authentication-authorization.md) -- [Managing Users in Nebari](./user-management.md) From e770eca6bf5c0abb939d837220749c8a2ffd3a9c Mon Sep 17 00:00:00 2001 From: vinicius douglas cerutti Date: Tue, 27 Jan 2026 20:33:47 -0300 Subject: [PATCH 11/11] add some styling updates to docs --- .../docs/how-tos/fine-grained-permissions.mdx | 133 ++++++++++-------- 1 file changed, 75 insertions(+), 58 deletions(-) diff --git a/docs/docs/how-tos/fine-grained-permissions.mdx b/docs/docs/how-tos/fine-grained-permissions.mdx index 5eb436c19..260f7425c 100644 --- a/docs/docs/how-tos/fine-grained-permissions.mdx +++ b/docs/docs/how-tos/fine-grained-permissions.mdx @@ -49,64 +49,6 @@ Creating or editing a role does nothing until it is assigned via **Role Mappings Roles in Nebari can stack, meaning that if a user is in one group with role `conda_store_admin` and another group with role `conda_store_viewer`, this user ultimately has the role `conda_store_admin`. In case of different levels of access originating from different groups, precedence is given to the highest level of access. See [Permission Precedence](#permission-precedence-and-troubleshooting) for details. ::: -## Managing Groups in Keycloak - -Keycloak is the identity and access management service used in Nebari. It allows you to create and manage users, groups, roles, and permissions. Groups in Keycloak are collections of users, and they can be assigned specific roles that grant permissions to access various services within Nebari. - -For detailed information on managing groups in Keycloak, refer to the [Keycloak documentation on Group Management](https://www.keycloak.org/docs/latest/server_admin/#_group_management). - -Below we outline the steps specific to Nebari. - -### Creating a New Group - -To create a new group in Keycloak: - -1. **Log in to Keycloak** as an administrator (usually the `root` user). -2. **Navigate to Groups**: Click on **Groups** in the left-hand menu. -3. **Create the Group**: Click the **New** button, enter an appropriate name for your new group (e.g., `conda-store-manager`), and save. - -If you wish to organize your groups hierarchically, you can create subgroups by selecting a parent group and adding a subgroup. - -**Example Workflow**: Creating a `conda-store-manager` group - -1. Click **New** in the upper-right corner under **Groups**. - -![Keycloak groups tab screenshot - user groups view](/img/how-tos/keycloak_groups.png) - -2. Give the new group an appropriate name. - -![Keycloak add group form - name field set to conda-store-manager](/img/how-tos/keycloak_new_group1.png) - -3. Under **Role Mapping**, add the appropriate **Client Roles** as needed; there should be no need to update the **Realm Roles**. - -![Keycloak group conda-store-manager form - role mappings tab focused with expanded client roles dropdown](/img/how-tos/keycloak_new_group2.png) - -In this example, the new group only has one mapped role, `conda_store_admin`; however, it's possible to attach multiple **Client Roles** to a single group. - -![Keycloak group conda-store-manager form - role mappings tab focused](/img/how-tos/keycloak_new_group3.png) - -### Managing Subgroups - -Subgroups allow you to create a hierarchical structure of groups, representing organizational units, projects, or teams. - -To manage subgroups: - -- **Creating Subgroups**: Select the parent group, navigate to the **Sub Groups** tab, and create a new subgroup. -- **Assigning Roles to Subgroups**: Assign roles to subgroups following the same process as regular groups. -- **Use Cases**: Organize groups hierarchically to reflect organizational structures (e.g., `engineering` → `data-science`, `engineering` → `backend`). - -For more information, see the [Keycloak documentation on Group Hierarchies](https://www.keycloak.org/docs/latest/server_admin/#group-hierarchies). - -### Deleting a Group - -To delete a group: - -1. **Navigate to Groups**: Click on **Groups** in the left-hand menu. -2. **Select the Group**: Click on the group you want to delete. -3. **Delete**: Click the **Delete** button and confirm. - -**Important**: Deleting a group does not delete the users in that group. Users will simply lose the permissions associated with that group. Any shared directories associated with the group will remain in the file system but will no longer be mounted for users. - ## Default Clients, Groups, and Permissions A fresh Nebari deployment comes with several custom Keycloak clients (services) enabled. You can manage these from **Clients** in the Keycloak admin console: @@ -273,6 +215,65 @@ import { Table } from '@site/src/components/MarkdownTable'; +## Managing Groups in Keycloak + +Keycloak is the identity and access management service used in Nebari. It allows you to create and manage users, groups, roles, and permissions. Groups in Keycloak are collections of users, and they can be assigned specific roles that grant permissions to access various services within Nebari. + +For detailed information on managing groups in Keycloak, refer to the [Keycloak documentation on Group Management](https://www.keycloak.org/docs/latest/server_admin/#_group_management). + +Below we outline the steps specific to Nebari. + +### Creating a New Group + +To create a new group in Keycloak: + +1. **Log in to Keycloak** as an administrator (usually the `root` user). +2. **Navigate to Groups**: Click on **Groups** in the left-hand menu. +3. **Create the Group**: Click the **New** button, enter an appropriate name for your new group (e.g., `conda-store-manager`), and save. + +If you wish to organize your groups hierarchically, you can create subgroups by selecting a parent group and adding a subgroup. + +**Example Workflow**: Creating a `conda-store-manager` group + +1. Click **New** in the upper-right corner under **Groups**. + +![Keycloak groups tab screenshot - user groups view](/img/how-tos/keycloak_groups.png) + +2. Give the new group an appropriate name. + +![Keycloak add group form - name field set to conda-store-manager](/img/how-tos/keycloak_new_group1.png) + +3. Under **Role Mapping**, add the appropriate **Client Roles** as needed; there should be no need to update the **Realm Roles**. + +![Keycloak group conda-store-manager form - role mappings tab focused with expanded client roles dropdown](/img/how-tos/keycloak_new_group2.png) + +In this example, the new group only has one mapped role, `conda_store_admin`; however, it's possible to attach multiple **Client Roles** to a single group. + +![Keycloak group conda-store-manager form - role mappings tab focused](/img/how-tos/keycloak_new_group3.png) + +### Managing Subgroups + +Subgroups allow you to create a hierarchical structure of groups, representing organizational units, projects, or teams. + +To manage subgroups: + +- **Creating Subgroups**: Select the parent group, navigate to the **Sub Groups** tab, and create a new subgroup. +- **Assigning Roles to Subgroups**: Assign roles to subgroups following the same process as regular groups. +- **Use Cases**: Organize groups hierarchically to reflect organizational structures (e.g., `engineering` → `data-science`, `engineering` → `backend`). + +For more information, see the [Keycloak documentation on Group Hierarchies](https://www.keycloak.org/docs/latest/server_admin/#group-hierarchies). + +### Deleting a Group + +To delete a group: + +1. **Navigate to Groups**: Click on **Groups** in the left-hand menu. +2. **Select the Group**: Click on the group you want to delete. +3. **Delete**: Click the **Delete** button and confirm. + +**Important**: Deleting a group does not delete the users in that group. Users will simply lose the permissions associated with that group. Any shared directories associated with the group will remain in the file system but will no longer be mounted for users. + + ## Creating Custom Roles with Scopes Beyond Nebari's default roles, you can also create your own with highly specific permissions. @@ -429,21 +430,27 @@ To add a role directly to a user: ### Example: Custom Role Scenarios +
+ **Scenario 1: Data Science Team Lead** - **Goal**: Admin access to team namespace, read-only to production - **Component**: `conda-store` - **Scopes**: `admin!namespace=data-science,viewer!namespace=production` +--- **Scenario 2: App Collaborator** - **Goal**: Share JupyterHub apps with specific users and groups - **Component**: `jupyterhub` - **Scopes**: `shares!user,shares!group,read:users:name,read:groups:name` +--- **Scenario 3: Cross-functional Analyst** - **Goal**: Developer access to multiple namespaces - **Component**: `conda-store` - **Scopes**: `developer!namespace=marketing,developer!namespace=sales,viewer!namespace=finance` +
+ ## Managing Users and Group Membership ### Adding Users to a Group @@ -563,6 +570,8 @@ When a user belongs to multiple groups or has multiple roles assigned, Nebari us ### Common Issues and Solutions +
+ #### Issue 1: Changes Not Taking Effect **Symptom**: User was added to a group or assigned a role, but still can't access resources. @@ -572,6 +581,8 @@ When a user belongs to multiple groups or has multiple roles assigned, Nebari us - Keycloak issues new access tokens on login with updated permissions - Refreshing the page is not sufficient +--- + #### Issue 2: User Has Too Many Permissions **Symptom**: User can access resources they shouldn't be able to. @@ -582,6 +593,8 @@ When a user belongs to multiple groups or has multiple roles assigned, Nebari us 3. Remember: highest permission wins, so remove the more permissive role/group 4. User must log out and back in +--- + #### Issue 3: Group Directory Not Appearing **Symptom**: Users in a group can't see the shared directory. @@ -592,6 +605,8 @@ When a user belongs to multiple groups or has multiple roles assigned, Nebari us 3. Check JupyterHub logs for errors 4. Verify the directory exists in the file system +--- + #### Issue 4: Custom Role Not Working **Symptom**: Created a custom role with scopes, but it has no effect. @@ -602,6 +617,8 @@ When a user belongs to multiple groups or has multiple roles assigned, Nebari us 3. Ensure role is assigned to a group or user via **Role Mappings** 4. User must log out and back in +
+ ### Testing Permissions To verify a user's permissions: