You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
75 lines
2.8 KiB
Python
75 lines
2.8 KiB
Python
"""Tornado handlers for the terminal emulator."""
|
|
# Copyright (c) Jupyter Development Team.
|
|
# Distributed under the terms of the Modified BSD License.
|
|
from __future__ import annotations
|
|
|
|
import typing as t
|
|
|
|
from jupyter_core.utils import ensure_async
|
|
from jupyter_server._tz import utcnow
|
|
from jupyter_server.auth.utils import warn_disabled_authorization
|
|
from jupyter_server.base.handlers import JupyterHandler
|
|
from jupyter_server.base.websocket import WebSocketMixin
|
|
from terminado.management import NamedTermManager
|
|
from terminado.websocket import TermSocket as BaseTermSocket
|
|
from tornado import web
|
|
|
|
from .base import TerminalsMixin
|
|
|
|
AUTH_RESOURCE = "terminals"
|
|
|
|
|
|
class TermSocket(TerminalsMixin, WebSocketMixin, JupyterHandler, BaseTermSocket):
|
|
"""A terminal websocket."""
|
|
|
|
auth_resource = AUTH_RESOURCE
|
|
|
|
def initialize( # type:ignore[override]
|
|
self, name: str, term_manager: NamedTermManager, **kwargs: t.Any
|
|
) -> None:
|
|
"""Initialize the socket."""
|
|
BaseTermSocket.initialize(self, term_manager, **kwargs)
|
|
TerminalsMixin.initialize(self, name)
|
|
|
|
def origin_check(self, origin: t.Any = None) -> bool:
|
|
"""Terminado adds redundant origin_check
|
|
Tornado already calls check_origin, so don't do anything here.
|
|
"""
|
|
return True
|
|
|
|
async def get(self, *args: t.Any, **kwargs: t.Any) -> None:
|
|
"""Get the terminal socket."""
|
|
user = self.current_user
|
|
|
|
if not user:
|
|
raise web.HTTPError(403)
|
|
|
|
# authorize the user.
|
|
if self.authorizer is None:
|
|
# Warn if an authorizer is unavailable.
|
|
warn_disabled_authorization() # type:ignore[unreachable]
|
|
elif not self.authorizer.is_authorized(self, user, "execute", self.auth_resource):
|
|
raise web.HTTPError(403)
|
|
|
|
if args[0] not in self.term_manager.terminals: # type:ignore[attr-defined]
|
|
raise web.HTTPError(404)
|
|
resp = super().get(*args, **kwargs)
|
|
if resp is not None:
|
|
await ensure_async(resp) # type:ignore[arg-type]
|
|
|
|
async def on_message(self, message: t.Any) -> None: # type:ignore[override]
|
|
"""Handle a socket message."""
|
|
await ensure_async(super().on_message(message)) # type:ignore[arg-type]
|
|
self._update_activity()
|
|
|
|
def write_message(self, message: t.Any, binary: bool = False) -> None: # type:ignore[override]
|
|
"""Write a message to the socket."""
|
|
super().write_message(message, binary=binary)
|
|
self._update_activity()
|
|
|
|
def _update_activity(self) -> None:
|
|
self.application.settings["terminal_last_activity"] = utcnow()
|
|
# terminal may not be around on deletion/cull
|
|
if self.term_name in self.terminal_manager.terminals:
|
|
self.terminal_manager.terminals[self.term_name].last_activity = utcnow() # type:ignore[attr-defined]
|