jupyterlab_server icon indicating copy to clipboard operation
jupyterlab_server copied to clipboard

Break up and make add_handlers overrideable

Open bollwyvl opened this issue 4 years ago • 6 comments

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 https://github.com/voila-dashboards/voila/pull/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.

bollwyvl avatar Mar 17 '21 13:03 bollwyvl

We discussed this at the Jupyter Server meeting today: https://github.com/jupyter-server/team-compass/issues/4#issuecomment-802058078

blink1073 avatar Mar 18 '21 16:03 blink1073

@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 avatar Dec 21 '21 08:12 fcollonval

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.

fcollonval avatar Dec 21 '21 12:12 fcollonval

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.

bollwyvl avatar Dec 21 '21 13:12 bollwyvl

Thanks Nick for the information and pointers.

fcollonval avatar Dec 22 '21 08:12 fcollonval

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?

martinRenou avatar Jan 06 '22 10:01 martinRenou