Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Break up and make add_handlers overrideable #167

Open
bollwyvl opened this issue Mar 17, 2021 · 6 comments · May be fixed by jupyter-server/jupyter_server#645
Open

Break up and make add_handlers overrideable #167

bollwyvl opened this issue Mar 17, 2021 · 6 comments · May be fixed by jupyter-server/jupyter_server#645
Assignees

Comments

@bollwyvl
Copy link
Contributor

Elevator Pitch

Break up handlers.add_handlers into something more easily overrideable.

Motivation

Presently, add_handlers is a single monolithic function that does... something to the app that goes in. Some features can be disabled by (un-)configuring the URL, but this wouldn't prevent things from getting added to a future release of the application, e.g. EmailHandler, etc. that a downstream may not want.

The sterling work over on voila-dashboards/voila#846 raises the point of only getting the handlers you want, irrespective of the jupyterlab_server version you'd pull in. In the case of voila, things like workspaces and extension manager points may not be useful, while certain parts of translations, themes, licenses (#161) would be important, but not the writeable ones.

Design ideas

Build up something like LabCapabilities which handles actually installing different handlers. It probably would still expose add_handlers, but would be more configurable (but perhaps not traitlets.config-urable).

class LabCapabilities(HasTraits):
    allowed_capabilities = ...
    blocked_capabilities = ... 
    def add_handlers(self, handlers, extension_app): ...

At an even lower level, these might allow for blocking individual methods, e.g. disabling POST, etc.

{WorkspacesHandler: ["post"]}

Alternatives

Perhaps this could land in jupyter_server, e.g. as an OpenAPI contract which the ultimate application implementation could levy against all the stuff that might get installed via e.g. extension.

@blink1073
Copy link
Contributor

We discussed this at the Jupyter Server meeting today: jupyter-server/team-compass#4 (comment)

@fcollonval
Copy link
Member

@bollwyvl I would like to address this one in jupyter server directly. But I have a doubt on the best way to achieve this.

Should this feature be implemented like a middleware filtering at runtime the request or at the handler registration? In the latter case, this means monkey patching the handler class if some verbs need to be blocked (like get is allowed but not post), isn't it?

@fcollonval
Copy link
Member

As the definition of an handler selector could be either a string or a Matcher object, the best generic approach leans towards a middleware approach.

@bollwyvl
Copy link
Contributor Author

I think the goal of this not to get into some kind of monkeypatch-off for the illusion of security, but rather to have a declarative way to impact how the server works at a deep level when one controls the environment.

As for the low-code part: maybe the best way would be leveraging an existing specification (OpenAPI)... or in the case of the whole jupyter architecture, two specifications, as we would need OpenAsync to describe kernel websockets.

The closest thing I've seen in the wild is connexion, but it's a bit heavy, but the idea is sound: hand-written specs not used for anything get stale and auto-generated ones need the system to actually be running, so really aren't a very good contract. If, instead, the spec was the definition of the system, and it could not even respond to things not available there, then it would be much clearer.

Perhaps JSON Patch of a spec could do the thing. So add_handler becomes register_module and patch_spec, and each individual extension to the system augments the master document. Once all the components have provided their patches, then the user/owner can apply their patches: they'd lack the ability to add any new implementations via moduleId, but could stop different routes.

monkey patching the handler class

In the past, I had done it within tornado after the match, but before HTTP method invocation in RequestHandler.prepare.

Run-time filtering in the matching process per-request would be even better, as the handler might do stuff in its constructor we don't want.

@fcollonval
Copy link
Member

Thanks Nick for the information and pointers.

@martinRenou
Copy link
Member

I am not sure I fully understand the issue here. It seems to me that one could manually pick the handlers they need? For example in voila-dashboards/voila#846, we don't need to use jupyterlab_server's add_handlers, we could probably manually add the labextensions handler, the theme handler and the mathjax one?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants