GithubHelp home page GithubHelp logo

jupyterhub-share-link's Introduction

JupyterHub Share Link

This is a new project that is still a work in progress. Please do not attempt to use it in production yet. Contributors welcome!

Demo

In this GIF, Alice logs in, right-clicks a notebook and chooses "Copy Shareable Link". A dialog box appears, saying:

For the next hour, any other user on this JupyterHub who has this link will be able to fetch a copy of your latest saved version of dask-examples/array.ipynb.

She copies the link and gives it to Bob. Then, on the right, Bob logs in and pastes the link into his browser. He is given a copy of Alice's notebook.

Demo

JupyterHub Compatibility

  • jupyterhub-share-link v0.0.1 is compatible with JupyterHub 1.0
  • juptyerhub-share-link v0.1.0 and requires JupyterHub 1.1 or higher. Specifically, it requires the one-line change made in jupyterhub#2755.

Uses and Limitations

This is for low-effort, short-term sharing between users who are on the same Hub.

The sender right-clicks a notebook (or any file) and clicks "Copy Shareable Link." The sender gives that link to any other user on the same Hub. When another user clicks the share link, the last saved version of the file is copied from the sender's notebook server to the recipient's. If the sender changes the file, the recipient can click the link again to make another copy reflecting the changes. After a given time interval, the link expires.

On Hubs that provide the user with options at spawn time, such as a container-based spawner, the share link encodes both the notebook and the options (e.g. the container image) that the sender was running that notebook in. The recipient will automatically be directed to a server spawned with the same options: the service finds a suitable existing one or spawns one if necessary. Thus the recipient has some assurance that they will be running the notebook in a compatible software environment.

This approach is not suitable for persistent sharing, such as galleries or lists of links to be maintained long term. For those use cases, it is better to encode software dependencies (as in a Binder repo) rather than relying on the availability of a specific image.

Try it โ€” with containers or without containers

This approach should be compatible with any spawner. Two examples are given here, a local process spawner and a container-based spawner.

With Containers

  1. Install JupyterHub.
pip install jupyterhub

The usual prequisites for installing JupyterHub apply. One expedient way to install node is:

pip install nodeenv
nodeenv -p
  1. Install using pip.

    pip install jupyterhub-share-link
    
  2. Install DockerSpawner.

    pip install dockerspawner
    
  3. Generate a key pair that will be used to sign and verify share links.

    # creates private.pem and public.pem in the current directory
    python -m jupyterhub_share_link.generate_keys
    
  4. Start JupyterHub using an example configuration provided in this repo.

    jupyterhub -f example_config_dockerspawner.py
    
  5. Log in with any username and password---for example, alice. (The DummyAuthenticator is used by this demo configuration.)

  6. Spawn a server using the default image, danielballan/base-notebook-with-image-spec-extension.

  7. Create and save a notebook Untitled.ipynb to share.

  8. Find Untitled.ipynb in the file browser and right-click it. A dialog box will appear. Click the button to copy the link.

  9. Log in as a different user and paste the shared link.

  10. The user will have a new server started running the same image as alice, and the notebook will be copied and opened.

Without Containers

  1. Install JupyterHub.
pip install jupyterhub

The usual prequisites for installing JupyterHub apply. One expedient way to install node is:

pip install nodeenv
nodeenv -p
  1. Install using pip.

    pip install jupyterhub-share-link
    
  2. Generate a key pair that will be used to sign and verify share links.

    # creates private.pem and public.pem in the current directory
    python -m jupyterhub_share_link.generate_keys
    
  3. Install the labextension into the user environment.

    # Disable the default share-file extension and register our custom one.
    jupyter labextension disable @jupyterlab/filebrowser-extension:share-file
    jupyter labextension install jupyterhub-share-link-labextension
    
  4. Start JupyterHub using an example configuration provided in this repo. (In order to be able to log in as multiple users, you will likely need to run this as root.)

    jupyterhub -f example_config_no_containers.py
    
  5. Log in as a system user and start the user's server.

  6. Create and save a notebook Untitled.ipynb to share.

  7. Find Untitled.ipynb in the file browser and right-click it. A dialog box will appear. Click the button to copy the link.

  8. Log in as a different user and paste the shared link.

  9. The notebook will be copied to that user's server and opened.

Design

This involves:

  • A stateless Hub Service (in this repository) with the routes:

    POST /create  # issue a shareable link
    GET /open  # open a shared link
    GET /inspect  # inspect a shared link
    GET /  # verion info
    
  • A public/private key pair that belong to the service, enabling it issue "share" links that it can verify the recipient has not tampered with.

  • A labextension that customizes the behavior of the 'Copy Share Link' context menu item, stored at danielballan/jupyterhub-share-link-labextension.

The file-copying occurs via the notebook's ContentsManager, so there is no need for users to be on the same filesystem. They only have to be on the same Hub.

Open Questions

  • Encrypt path so that directory structure is not leaked to recipient?

jupyterhub-share-link's People

Contributors

danielballan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

jupyterhub-share-link's Issues

Support for KubeSpawner

Hi @danielballan,

I am interested to use your extension in our Zero2JupyterHub cluster with KubeSpawner.

Are there any pieces in your code which limits it to the local and Docker spawners?
Maybe we can attempt to make it independent from the spawner itself?

Error 500 when a user opens a shared link to a notebook

I followed your instructions and installed jupyterhub-share-link but I keep getting 500 error when I open a link to a shared notebook. I would appreciate any suggestions. Thank you.

jupyterhub 1.1.0
jupyterhub-share-link 0.1.1
jupyterhub-share-link-labextension v0.3.0

Here is the error in jupyterhub_share_link.log (the token and hostname are masked below):

[E 200211 14:03:55 web:1788] Uncaught exception GET /hub/services/share-link/open?token=*************************** (127.0.0.1) HTTPServerRequest(protocol='http', host='*********', method='GET', uri='/hub/services/share-link/open?token=**************************', version='HTTP/1.1', remote_ip='127.0.0.1') Traceback (most recent call last): File "/opt/conda/lib/python3.6/site-packages/tornado/web.py", line 1699, in _execute result = await result File "/opt/conda/lib/python3.6/site-packages/jupyterhub_share_link/run.py", line 195, in get resp = await AsyncHTTPClient().fetch(req) tornado.httpclient.HTTPClientError: HTTP 404: Not Found [E 200211 14:03:55 web:2246] 500 GET /hub/services/share-link/open?token=**************************************

Cannot assign requested address(or tornado.iostream.StreamClosedError: Stream is closed)

Hi,
I'm testing this locally. I generated a url and when putting the url in the browser for another user, I get a 500 error.
Inspecting the logs, I see

jhub   | [I 200214 04:12:24 run:108] Honoring token {'user': 'rabraham', 'path': 'Untitled1.ipynb', 'opts': {'image': 'localhost:5000/base-notebook-with-jupyterhub-share-labextension'}, 'exp': 1581657129}
jhub   | [I 2020-02-14 04:12:24.167 JupyterHub log:174] 200 GET /hub/api/users/rabraham ([email protected]) 19.66ms
jhub   | [I 2020-02-14 04:12:24.266 JupyterHub log:174] 200 GET /hub/api/users/rabraham ([email protected]) 24.02ms
jhub   | [I 2020-02-14 04:12:24.324 JupyterHub log:174] 200 GET /hub/api/users/rabraham_r ([email protected]) 30.37ms
jhub   | [E 200214 04:12:24 web:1788] Uncaught exception GET /services/share-link/open?token=ZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKU1V6STFOaUo5LmV5SjFjMlZ5SWpvaWNtRmljbUZvWVcwaUxDSndZWFJvSWpvaVZXNTBhWFJzWldReExtbHdlVzVpSWl3aWIzQjBjeUk2ZXlKcGJXRm5aU0k2SW14dlkyRnNhRzl6ZERvMU1EQXdMMkpoYzJVdGJtOTBaV0p2YjJzdGQybDBhQzFxZFhCNWRHVnlhSFZpTFhOb1lYSmxMV3hoWW1WNGRHVnVjMmx2YmlKOUxDSmxlSEFpT2pFMU9ERTJOVGN4TWpsOS5KemF5Y3RPNFE3OWo4VXFjbGJNTWh1SERvUXVYYjl1d0YyNUVVMWhQWmJOTDZ2dTFQMko3R0JlZ09TNzcwZUt1V2lZa0JwelBLTy15YkJzc2QyN0ZEMmpBc1hZTk00NDhxOHA1c050VHg0ZERPNGJON3ZHNTNHb2dJWUdIdV9jcmpIZzRTUEpyZW9pcWxqc2tOSHJ4VU1Yb2hGQ2Qwbm1mTExmVFRScW5PWGNQVjZDUWFZWW8yQXAyZEhnR0FVN1ZPOVEyMEtRZHVrOExKX19UdG1ZQ0xQUnZVLUVVaDVvT2dyOGk3ZHVPYnVDU1dhT0VxUWE3T1dXYzJnT0VSQ21QUWR0RE9jLUd3UHdLUFRrZWhtSzJHaE5sOUFDaEk4MFRjSEZ6UnZ2czV4d2hLczRjbi1iWTRPalZEZHp2ZUVLWm9CNGdHV2tzaHR3R2hoUFNUdmtPMnc= (127.0.0.1)
jhub   |     HTTPServerRequest(protocol='http', host='localhost:30100', method='GET', uri='/services/share-link/open?token=ZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKU1V6STFOaUo5LmV5SjFjMlZ5SWpvaWNtRmljbUZvWVcwaUxDSndZWFJvSWpvaVZXNTBhWFJzWldReExtbHdlVzVpSWl3aWIzQjBjeUk2ZXlKcGJXRm5aU0k2SW14dlkyRnNhRzl6ZERvMU1EQXdMMkpoYzJVdGJtOTBaV0p2YjJzdGQybDBhQzFxZFhCNWRHVnlhSFZpTFhOb1lYSmxMV3hoWW1WNGRHVnVjMmx2YmlKOUxDSmxlSEFpT2pFMU9ERTJOVGN4TWpsOS5KemF5Y3RPNFE3OWo4VXFjbGJNTWh1SERvUXVYYjl1d0YyNUVVMWhQWmJOTDZ2dTFQMko3R0JlZ09TNzcwZUt1V2lZa0JwelBLTy15YkJzc2QyN0ZEMmpBc1hZTk00NDhxOHA1c050VHg0ZERPNGJON3ZHNTNHb2dJWUdIdV9jcmpIZzRTUEpyZW9pcWxqc2tOSHJ4VU1Yb2hGQ2Qwbm1mTExmVFRScW5PWGNQVjZDUWFZWW8yQXAyZEhnR0FVN1ZPOVEyMEtRZHVrOExKX19UdG1ZQ0xQUnZVLUVVaDVvT2dyOGk3ZHVPYnVDU1dhT0VxUWE3T1dXYzJnT0VSQ21QUWR0RE9jLUd3UHdLUFRrZWhtSzJHaE5sOUFDaEk4MFRjSEZ6UnZ2czV4d2hLczRjbi1iWTRPalZEZHp2ZUVLWm9CNGdHV2tzaHR3R2hoUFNUdmtPMnc=', version='HTTP/1.1', remote_ip='127.0.0.1')
jhub   |     Traceback (most recent call last):
jhub   |       File "/usr/local/lib/python3.6/dist-packages/tornado/tcpclient.py", line 143, in on_connect_done
jhub   |         stream = future.result()
jhub   |     tornado.iostream.StreamClosedError: Stream is closed
jhub   |
jhub   |     During handling of the above exception, another exception occurred:
jhub   |
jhub   |     Traceback (most recent call last):
jhub   |       File "/usr/local/lib/python3.6/dist-packages/tornado/web.py", line 1699, in _execute
jhub   |         result = await result
jhub   |       File "/usr/local/lib/python3.6/dist-packages/jupyterhub_share_link/run.py", line 195, in get
jhub   |         resp = await AsyncHTTPClient().fetch(req)
jhub   |       File "/usr/local/lib/python3.6/dist-packages/tornado/iostream.py", line 1200, in connect
jhub   |         self.socket.connect(address)
jhub   |     OSError: [Errno 99] Cannot assign requested address

On a side note, as user2(the person who user1 gives the link too) , I have to login and spawn my notebook server before I can paste the url in the browser.

Big Problem - Doesn't work - Help me

Hello, I have tried your tool. Except that it doesn't work. I have tried everything but I can't get it to work. I would be curious to know how it works

Thanls for your help

Create custom error pages

Currently, accessing an expired like creates a sensible message in the logs

tornado.httpclient.HTTPClientError: HTTP 403: Sharing link has expired. Ask for a fresh link.

but shows the user a vanilla 500 Internal Server Error.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.