handlers.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. """Tornado handlers for the terminal emulator."""
  2. # Copyright (c) Jupyter Development Team.
  3. # Distributed under the terms of the Modified BSD License.
  4. from __future__ import annotations
  5. import typing as t
  6. from jupyter_core.utils import ensure_async
  7. from jupyter_server._tz import utcnow
  8. from jupyter_server.auth.utils import warn_disabled_authorization
  9. from jupyter_server.base.handlers import JupyterHandler
  10. from jupyter_server.base.websocket import WebSocketMixin
  11. from terminado.management import NamedTermManager
  12. from terminado.websocket import TermSocket as BaseTermSocket
  13. from tornado import web
  14. from .base import TerminalsMixin
  15. AUTH_RESOURCE = "terminals"
  16. class TermSocket(TerminalsMixin, WebSocketMixin, JupyterHandler, BaseTermSocket):
  17. """A terminal websocket."""
  18. auth_resource = AUTH_RESOURCE
  19. def initialize( # type:ignore[override]
  20. self, name: str, term_manager: NamedTermManager, **kwargs: t.Any
  21. ) -> None:
  22. """Initialize the socket."""
  23. BaseTermSocket.initialize(self, term_manager, **kwargs)
  24. TerminalsMixin.initialize(self, name)
  25. def origin_check(self, origin: t.Any = None) -> bool:
  26. """Terminado adds redundant origin_check
  27. Tornado already calls check_origin, so don't do anything here.
  28. """
  29. return True
  30. async def get(self, *args: t.Any, **kwargs: t.Any) -> None:
  31. """Get the terminal socket."""
  32. user = self.current_user
  33. if not user:
  34. raise web.HTTPError(403)
  35. # authorize the user.
  36. if self.authorizer is None:
  37. # Warn if an authorizer is unavailable.
  38. warn_disabled_authorization() # type:ignore[unreachable]
  39. elif not await ensure_async(
  40. self.authorizer.is_authorized(self, user, "execute", self.auth_resource)
  41. ):
  42. raise web.HTTPError(403)
  43. if args[0] not in self.term_manager.terminals: # type:ignore[attr-defined]
  44. raise web.HTTPError(404)
  45. resp = super().get(*args, **kwargs)
  46. if resp is not None:
  47. await ensure_async(resp) # type:ignore[arg-type]
  48. async def on_message(self, message: t.Any) -> None: # type:ignore[override]
  49. """Handle a socket message."""
  50. await ensure_async(super().on_message(message)) # type:ignore[arg-type]
  51. self._update_activity()
  52. def write_message(self, message: t.Any, binary: bool = False) -> None: # type:ignore[override]
  53. """Write a message to the socket."""
  54. super().write_message(message, binary=binary)
  55. self._update_activity()
  56. def _update_activity(self) -> None:
  57. self.application.settings["terminal_last_activity"] = utcnow()
  58. # terminal may not be around on deletion/cull
  59. if self.term_name in self.terminal_manager.terminals:
  60. self.terminal_manager.terminals[self.term_name].last_activity = utcnow() # type:ignore[attr-defined]