Adding a UI to let users share their servers to other users and groups
Hello,
I've added the necessary code related to my issue #437.
It replaces the default link share UI in cases where Jupyter is running behind a Hub that allows the creation of shares. It enables users to:
- Create a share to the current server for a user or a group using a search box.
- Revoke a share for a user or a group.
The permissions needed (both in Hub and Lab) are:
"shares!user"to manage users' shares."read:users:name"to create shares by designating a user by their name (TODO: check if it's really necessary iflist:usersis already in scope?)."list:users"(not mandatory) to list users for the search box."list:groups"(not mandatory) to list groups for the search box."servers!user"(not mandatory) to allow other users to start and stop the shared server
My code does not:
- Work if neither
list:usersnorlist:groupsis in scope. I wanted to add an option to invite someone by typing their exact username, without having to search. - Manage share codes, which may be useful in some scenarios.
This development has been made for my own internal purposes, but I think it could be useful to everyone. Comments and critiques are welcome!
Amazing work! I think determining the server owner should be more robust:
https://github.com/Hyrla/jupyter-collaboration/blob/1743de5c50dcd6372dd7926cfc72b96970544f02/packages/collaboration/src/sharedlink.ts#L48
This does not work if c.JupyterHub.base_url is set.
Amazing work! I think determining the server owner should be more robust:
https://github.com/Hyrla/jupyter-collaboration/blob/1743de5c50dcd6372dd7926cfc72b96970544f02/packages/collaboration/src/sharedlink.ts#L48
This does not work if
c.JupyterHub.base_urlis set.
Thank you for your feedback. Do you have any suggestion to determine the server owner with a cleaner way?
I think, if the server is running under JupyterHub, one can extract JupyterHub.base_url from PageConfig.getOption('hubPrefix') by removing the trailing /hub/.
There also appears to be an option PageConfig.getOption('hubServerUser'), which is probably what you need?
Similarly, serverName can be extracted from PageConfig.getOption('hubServerName').
You can see all available options of PageConfig by inspecting the jupyter-config-data tag of the HTML source of the lab page.
See https://jupyterlab.readthedocs.io/en/latest/api/functions/coreutils.PageConfig.getOption.html
You can see all available options of
PageConfigby inspecting thejupyter-config-datatag of the HTML source of the lab page. See https://jupyterlab.readthedocs.io/en/latest/api/functions/coreutils.PageConfig.getOption.html
Omg thank you, I searched hours for this list of PageConfig
@ykazakov I've pushed the fix, thanks again for your feedback. Could you please try if it works on your Hub? It works on mine but c.JupyterHub.base_url is not set there.
@Hyrla I tested, now it works with c.JupyterHub.base_url set!
However, these options are probably available only if running under JupyterHub, so the variables should probably be set after the check is done:
https://github.com/jupyterlab/jupyter-collaboration/blob/0f9e77ed4d839a59757295f3e499a1810dc8a3d8/packages/collaboration/src/sharedlink.ts#L56-L61
Also, if running behind a JupyterHub, shouldn't all these options be set, not just one of them? I would suggest to instead check the presence of (all) options that you really use for the dialog.
@ykazakov thank you for your feedback. I am pushing a fix regarding the two getOptions() that are misplaced. Regarding the detection of JupyterHub, not all these variables are necessarily set. I did set back the original condition as seen before my pull request: https://github.com/jupyterlab/jupyter-collaboration/blob/1c23b688ba22c1911d3ee93542e1da6e67f0514a/packages/collaboration/src/sharedlink.ts#L53
I have been further testing, and in general, the dialog works great! 👍
Some (mostly minor) comments:
- In the search list one cannot tell users and groups apart. If there is a group named exactly as the user, it is not clear which one is wich.
- Clicking on the item in the list immediately creates a share. Perhaps it would make sense to have a confirmation dialogue to prevent unintentional shares (particularly for groups).
- What happens if the list is very long (hundreds of users)? Perhaps wait until something is typed in the search box and the number of matches is narrowed down. See the nextcloud share dialogue.
- If the user does not have a permission to share a server (example: user Alice shares her server with user Bob, and user Bob tries to re-share the server of Alice), the share button results in the standard dialog, which would not work even if to include the token. Perhaps instead, one should show a dialog that the user does not have permission to share this server (if in doubt, ask admin), or even better, not to show the share icon at all.
This should be where the page config vars originate: https://github.com/jupyterhub/jupyterhub/blob/5.2.1/jupyterhub/singleuser/extension.py#L216-L234
I have been further testing, and in general, the dialog works great! 👍
Thank you!
Some (mostly minor) comments:
1. In the search list one cannot tell users and groups apart. If there is a group named exactly as the user, it is not clear which one is wich.
Yes you're right, I will add a "Group" prefix like in the shares list
2. Clicking on the item in the list immediately creates a share. Perhaps it would make sense to have a confirmation dialogue to prevent unintentional shares (particularly for groups).
Do you have any example snippets of code to create such a dialogue ? I wasn't able to find one (I am not an expert at all in TS nor Jupyter extension)
3. What happens if the list is very long (hundreds of users)? Perhaps wait until something is typed in the search box and the number of matches is narrowed down. See the [nextcloud share dialogue](https://nextcloud.com/blog/sharing-in-nextcloud/).
On my Hub there are about 350 users and it works like a charm. I am using the API with pagination, so there is no big query with every users at a single time. But if there is thousands of users it could be a problem. As far as I know, there is no Hub API to search for users or group?
4. If the user does not have a permission to share a server (example: user Alice shares her server with user Bob, and user Bob tries to re-share the server of Alice), the share button results in the standard dialog, which would not work even if to include the token. Perhaps instead, one should show a dialog that the user does not have permission to share this server (if in doubt, ask admin), or even better, not to show the share icon at all.
Like for 2., I don't know (yet) a way to create such dialogue but I will add it asap
This should be where the page config vars originate: https://github.com/jupyterhub/jupyterhub/blob/5.2.1/jupyterhub/singleuser/extension.py#L216-L234
Thank you!
Amazing, thank you for this work! This will be really valuable for a workshop @fperez , myself, and others will be teaching at AGU this Winter. If I can help out with anything, maybe typing? let me know :)
Hi @krassowski, thank you for your valuable feedback! I fixed my code regarding most of your suggestions. I'm a beginner in TypeScript and not really sure about typing this stuff. @mfisher87 if you have some time, your help is welcome :)
I will push typing updates.
It looks like both /user and /groups return kind: 'user' and kind: 'group' respectively, is the addition of type needed?
https://jupyterhub.readthedocs.io/en/stable/reference/rest-api.html#operation/get-users https://jupyterhub.readthedocs.io/en/stable/reference/rest-api.html#operation/get-groups
Thank you very much @krassowski for your feedbacks and help on this PR!
4.1.0 RC0 is available for testing:
- https://github.com/jupyterlab/jupyter-collaboration/releases/tag/v4.1.0rc0
- https://pypi.org/project/jupyter-collaboration/4.1.0rc0/
I have installed 4.1.0 RC0 on my school Jupyter Hub and everything seems to work fine
@krassowski how do you think we should handle the documentation of this feature? Especially regarding the permissions needed to make it working. On my Hub, I've simply added this to my Z2JH yaml config:
hub:
[...]
loadRoles:
share:
description: Allow users to manage shares on their server
scopes: [shares!user, read:users:name, list:users, servers!user]
groups: [d35c2143-xxxx, 6bdd8535-xxxx, c481a9cf-xxxx, c1927c2a-xxxx, ba715d4d-xxxx, 6c7c5e80-xxxx, 9e98f201-xxxx, 4e3c0034-xxxx]
Then I've added this extra config to give these permissions to the lab's scope:
hub:
[...]
extraConfig:
customLabScopesOptions: |
c.Spawner.oauth_client_allowed_scopes = ["read:users:name", "shares!user", "list:users", "servers!user"]
Finally, I've added this extra config to remove the oauth confirmation page (which I found not necessary for my use case):
hub:
[...]
extraConfig:
[...]
disableConfirmation: |
# Désactivation de la page de confirmation relou
import jupyterhub.apihandlers.auth
class DisableConfirmationOAuthAuthorizeHandler(
jupyterhub.apihandlers.auth.OAuthAuthorizeHandler
):
def needs_oauth_confirm(self, *args, **kwargs):
return False
for i, (route, handler) in enumerate(jupyterhub.apihandlers.default_handlers):
if route == "/api/oauth2/authorize":
jupyterhub.apihandlers.default_handlers[i] = (route, DisableConfirmationOAuthAuthorizeHandler)
break
If needed, here is my full config: https://gitlab.esiea.fr/gauthier.heiss/esiea-jupyter-image/-/blob/main/JupyterHub.yaml
I am pretty sure this config is not the perfect way to go and could be much simplier and secure.
I would defer to @manics on that.
You could update the docs on https://jupyterhub.readthedocs.io/en/stable/tutorial/collaboration-users.html https://github.com/jupyterhub/jupyterhub/blob/main/docs/source/tutorial/collaboration-users.md
Disabling oauth isn't recommended unless all users fully trust each other, so I wouldn't include that.
You could update the docs on https://jupyterhub.readthedocs.io/en/stable/tutorial/collaboration-users.html https://github.com/jupyterhub/jupyterhub/blob/main/docs/source/tutorial/collaboration-users.md
Disabling oauth isn't recommended unless all users fully trust each other, so I wouldn't include that.
Thank you for your reply. Concerning oauth, is there other any to handle it? Of course disabling to confirmation UI is not recommended at all, but otherwise every use has to accept the particular sharing role permissions when starting a server, even if it's not meant to be collaborative