Real-time collaboration z2jh

Hello,

I try to deploy real-time collaboration in here Real-time collaboration without impersonation — JupyterHub documentation with z2jh. Here is part of my config:

hub:
  extraConfig:
    auth: |
        from jupyterhub.auth import Authenticator
        from tornado import gen
        import re
        class MyCustomAuthenticator(Authenticator):
          @classmethod
          def normalize_username(cls, username):
            return re.sub(r"[@.]+","-",username.lower())
          @gen.coroutine
          def authenticate(self, handler, data):
            return self.normalize_username(data['username'])
        c.JupyterHub.authenticator_class = MyCustomAuthenticator

    pre_spawn_hook: |
        def pre_spawn_hook(spawner):
          group_names = {group.name for group in spawner.user.groups}
          if "collaborative" in group_names:
            spawner.log.info(f"Enabling RTC for user {spawner.user.name}")
            spawner.args.append("--LabApp.collaborative=True")
          print(f'#### group_names: {group_names} spawner.args: {spawner.args} #####')
          # drop container capabilities for security reasons
          spawner.container_security_context = {"capabilities": {"drop": ["ALL"]}}

        c.KubeSpawner.pre_spawn_hook = pre_spawn_hook
    auth_state_hook: |

        def userdata_hook(spawner, auth_state):
          spawner.userdata = auth_state
        c.KubeSpawner.auth_state_hook = userdata_hook
    init: |
      project_config = {
          "projects": {
            "vox": {
              "members": ["vex", "vax", "pike"]
            },
            "mighty": {
              "members": ["fjord", "beau", "jester"]
            }
          }
        }
       c.JupyterHub.load_roles = []
        c.JupyterHub.load_groups = {
          # collaborative accounts get added to this group
          # so it's easy to see which accounts are collaboration accounts
          "collaborative": [],
        }
        for project_name, project in project_config["projects"].items():
            # get the members of the project
            members = project.get("members", [])
            print(f"Adding project {project_name} with members {members}")
            # add them to a group for the project
            c.JupyterHub.load_groups[project_name] = members

            allowed_users.update(members)
            # define a new user for the collaboration
            collab_user = f"{project_name}-collab"
            # add the collab user to the 'collaborative' group
            # so we can identify it as a collab account
            c.JupyterHub.load_groups["collaborative"].append(collab_user)

            # finally, grant members of the project collaboration group
            # access to the collab user's server,
            # and the admin UI so they can start/stop the server
            c.JupyterHub.load_roles.append(
                {
                    "name": f"collab-access-{project_name}",
                    "scopes": [
                        f"access:servers!user={collab_user}",
                        f"admin:servers!user={collab_user}",
                        "admin-ui",
                        f"list:users!user={collab_user}",
                    ],
                    "groups": [project_name],
                }
            )
singleuser:
    defaultUrl: /lab
    image:
      name: jupyter/datascience-notebook
      tag: latest
      pullPolicy: IfNotPresent
    storage:
      type: none
      extraVolumes:
        - name: jupyter-notebook-config
          configMap:
            name: jupyter-notebook-config
        - name: exchange
          persistentVolumeClaim:
            claimName: exchange
      extraVolumeMounts:
        - name: jupyter-notebook-config
          mountPath: '/usr/local/etc/jupyter/'
          readOnly: true
        - name: exchange
          mountPath: '/home/jovyan'

(the rest of the config is usual config.)

But it doesn’t work for me (there’s no real-time collaboration). I was wondering how I can fix it? Is it related to jupyterhub version? How can I get my jupyterhub version & jupyterlab version?
Thanks in advance for your effort

best

If I put a print(f'#### group_names: {group_names} spawner.args: {spawner.args} #####') inside the pre_spawner_hook, I get ##### group_names: {'vox'} spawner.args: [] ##### when I log in as user vex!

Is it expected?

here is all the roles and groups from /usr/local/etc/jupyterhub/jupyterhub_config.py in the hub pod:

JupyterHub
 .load_groups = {   'collaborative': ['vox-collab', 'mighty-collab'],
    'mighty': ['fjord', 'beau', 'jester'],
    'vox': ['vex', 'vax', 'pike']}
  .load_roles = [   {   'groups': ['vox'],
        'name': 'collab-access-vox',
        'scopes': [   'access:servers!user=vox-collab',
                      'admin:servers!user=vox-collab', 'admin-ui',
                      'list:users!user=vox-collab']},
    {   'groups': ['mighty'],
        'name': 'collab-access-mighty',
        'scopes': [   'access:servers!user=mighty-collab',
                      'admin:servers!user=mighty-collab', 'admin-ui',
                      'list:users!user=mighty-collab']}]

KubeSpawner
  .allow_privilege_escalation = False
  .auth_state_hook = <function userdata_hook at 0x7ffb8bc5d1c0>
  .common_labels = {   'app': 'jupyterhub',
    'chart': 'jupyterhub-3.2.1',
    'heritage': 'jupyterhub',
    'release': 'collab'}

The example in the JupyterHub docs is quite comprehensive. To help debug things can you start with a minimal RTC example, e.g. Use the same permissions for all users, and remove as much dynamic config as you can? Once that’s working you can add back the additional configuration.

Thank you very much for your response. I couldn’t find any minimal example in z2jh. The example in the website seems to be in normal config.py file. I was wondering, would you mean that I remove the dropped capabilities from containers? Does RTC need specific permissions, e.g., networking?

This works for me with Z2jh version 3.2.1:

hub:
  config:
    JupyterHub:
      default_url: /hub/home
      load_groups:
        collaboration:
          - collab
          - user1
          - user2
      load_roles:
        - name: collab-access
          description: Access each others servers
          scopes:
            - access:servers!user=collab
            - admin:servers!user=collab
            - admin-ui
            - list:users!user=collab
          groups:
            - collaboration
    DummyAuthenticator:
      allowed_users:
        - collab
        - user1
        - user2

singleuser:
  image:
    name: example/minimal-rtc
    tag: latest

where the example/minimal-rtc:latest image is

FROM quay.io/jupyter/minimal-notebook:2024-02-24
RUN mamba install jupyter-collaboration

Thank you for your response. Now the collaboration icon appeared with the new image but I still don’t see any collaborations even after following your script (the only difference is that instead of user1 and user2 I used vex and vax).

May I see the rest of your config including volume mounts and others?

hub:
  config:
    JupyterHub:
      default_url: /hub/home
      load_groups:
        collaboration:
          - collab
          - user1
          - user2
      load_roles:
        - name: collab-access
          description: Access each others servers
          scopes:
            - access:servers!user=collab
            - admin:servers!user=collab
            - admin-ui
            - list:users!user=collab
          groups:
            - collaboration
    DummyAuthenticator:
      allowed_users:
        - collab
        - user1
        - user2

singleuser:
  image:
    name: docker.io/manics/minimal-rtc
    tag: latest
  storage:
    type: none

proxy:
  service:
    type: NodePort
    nodePorts:
      http: 31080

Login as user1, go to the admin panel, start collab’s server.

Then go to http://HOSTNAME/user/collab

In another browser login as user2, and go to http://HOSTNAME/user/collab

1 Like

Thanks a lot for the complete script. it worked. the problem was I wasn’t going to this URL /user/collab

1 Like

I can confirm, this works for me as well using the collab server. However, I wonder whether it is possible for user2 to access a notebook shared by user1?
The functionality for this logic seems to be implemented, but I can’t get it to work.

Sharing links is one piece, but users must be granted permission to access the server as well.

This can be done either by granting users the access:servers scope in configuration, or (in JupyterHub 5), users may grant each other permission to access their servers.