LocalAuthorizationServer.py 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. # Copyright (c) 2020 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. import threading
  4. from typing import Any, Callable, Optional, TYPE_CHECKING
  5. from UM.Logger import Logger
  6. got_server_type = False
  7. try:
  8. from cura.OAuth2.AuthorizationRequestServer import AuthorizationRequestServer
  9. from cura.OAuth2.AuthorizationRequestHandler import AuthorizationRequestHandler
  10. got_server_type = True
  11. except PermissionError: # Bug in http.server: Can't access MIME types. This will prevent the user from logging in. See Sentry bug Cura-3Q.
  12. Logger.error("Can't start a server due to a PermissionError when starting the http.server.")
  13. if TYPE_CHECKING:
  14. from cura.OAuth2.Models import AuthenticationResponse
  15. from cura.OAuth2.AuthorizationHelpers import AuthorizationHelpers
  16. class LocalAuthorizationServer:
  17. ## The local LocalAuthorizationServer takes care of the oauth2 callbacks.
  18. # Once the flow is completed, this server should be closed down again by
  19. # calling stop()
  20. # \param auth_helpers An instance of the authorization helpers class.
  21. # \param auth_state_changed_callback A callback function to be called when
  22. # the authorization state changes.
  23. # \param daemon Whether the server thread should be run in daemon mode.
  24. # Note: Daemon threads are abruptly stopped at shutdown. Their resources
  25. # (e.g. open files) may never be released.
  26. def __init__(self, auth_helpers: "AuthorizationHelpers",
  27. auth_state_changed_callback: Callable[["AuthenticationResponse"], Any],
  28. daemon: bool) -> None:
  29. self._web_server = None # type: Optional[AuthorizationRequestServer]
  30. self._web_server_thread = None # type: Optional[threading.Thread]
  31. self._web_server_port = auth_helpers.settings.CALLBACK_PORT
  32. self._auth_helpers = auth_helpers
  33. self._auth_state_changed_callback = auth_state_changed_callback
  34. self._daemon = daemon
  35. ## Starts the local web server to handle the authorization callback.
  36. # \param verification_code The verification code part of the OAuth2 client identification.
  37. # \param state The unique state code (to ensure that the request we get back is really from the server.
  38. def start(self, verification_code: str, state: str) -> None:
  39. if self._web_server:
  40. # If the server is already running (because of a previously aborted auth flow), we don't have to start it.
  41. # We still inject the new verification code though.
  42. self._web_server.setVerificationCode(verification_code)
  43. return
  44. if self._web_server_port is None:
  45. raise Exception("Unable to start server without specifying the port.")
  46. Logger.log("d", "Starting local web server to handle authorization callback on port %s", self._web_server_port)
  47. # Create the server and inject the callback and code.
  48. if got_server_type:
  49. self._web_server = AuthorizationRequestServer(("0.0.0.0", self._web_server_port), AuthorizationRequestHandler)
  50. self._web_server.setAuthorizationHelpers(self._auth_helpers)
  51. self._web_server.setAuthorizationCallback(self._auth_state_changed_callback)
  52. self._web_server.setVerificationCode(verification_code)
  53. self._web_server.setState(state)
  54. # Start the server on a new thread.
  55. self._web_server_thread = threading.Thread(None, self._web_server.serve_forever, daemon = self._daemon)
  56. self._web_server_thread.start()
  57. ## Stops the web server if it was running. It also does some cleanup.
  58. def stop(self) -> None:
  59. Logger.log("d", "Stopping local oauth2 web server...")
  60. if self._web_server:
  61. try:
  62. self._web_server.server_close()
  63. except OSError:
  64. # OS error can happen if the socket was already closed. We really don't care about that case.
  65. pass
  66. self._web_server = None
  67. self._web_server_thread = None