mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
WIP - feat(acl): implement ACL handling and token encryption in BECWidgetsCLIServer and client utilities
This commit is contained in:
@ -11,9 +11,11 @@ from contextlib import contextmanager
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import msgpack
|
||||||
from bec_lib.endpoints import MessageEndpoints
|
from bec_lib.endpoints import MessageEndpoints
|
||||||
from bec_lib.logger import bec_logger
|
from bec_lib.logger import bec_logger
|
||||||
from bec_lib.utils.import_utils import isinstance_based_on_class_name, lazy_import, lazy_import_from
|
from bec_lib.utils.import_utils import isinstance_based_on_class_name, lazy_import, lazy_import_from
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
|
|
||||||
import bec_widgets.cli.client as client
|
import bec_widgets.cli.client as client
|
||||||
from bec_widgets.cli.auto_updates import AutoUpdates
|
from bec_widgets.cli.auto_updates import AutoUpdates
|
||||||
@ -67,7 +69,14 @@ def _get_output(process, logger) -> None:
|
|||||||
logger.error(f"Error reading process output: {str(e)}")
|
logger.error(f"Error reading process output: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
def _start_plot_process(gui_id: str, gui_class: type, config: dict | str, logger=None) -> None:
|
def _start_plot_process(
|
||||||
|
gui_id: str,
|
||||||
|
gui_class: type,
|
||||||
|
config: dict | str,
|
||||||
|
acl_data: bytes | None = None,
|
||||||
|
token: bytes | None = None,
|
||||||
|
logger=None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Start the plot in a new process.
|
Start the plot in a new process.
|
||||||
|
|
||||||
@ -84,6 +93,8 @@ def _start_plot_process(gui_id: str, gui_class: type, config: dict | str, logger
|
|||||||
|
|
||||||
env_dict = os.environ.copy()
|
env_dict = os.environ.copy()
|
||||||
env_dict["PYTHONUNBUFFERED"] = "1"
|
env_dict["PYTHONUNBUFFERED"] = "1"
|
||||||
|
env_dict["BEC_GUI_ACL"] = acl_data or b""
|
||||||
|
env_dict["BEC_GUI_TOKEN"] = token or b""
|
||||||
|
|
||||||
if logger is None:
|
if logger is None:
|
||||||
stdout_redirect = subprocess.DEVNULL
|
stdout_redirect = subprocess.DEVNULL
|
||||||
@ -179,6 +190,7 @@ class BECGuiClient(RPCBase):
|
|||||||
self._gui_started_event = threading.Event()
|
self._gui_started_event = threading.Event()
|
||||||
self._process = None
|
self._process = None
|
||||||
self._process_output_processing_thread = None
|
self._process_output_processing_thread = None
|
||||||
|
self._fernet = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def windows(self):
|
def windows(self):
|
||||||
@ -263,6 +275,18 @@ class BECGuiClient(RPCBase):
|
|||||||
self._do_show_all()
|
self._do_show_all()
|
||||||
self._gui_started_event.set()
|
self._gui_started_event.set()
|
||||||
|
|
||||||
|
# def _update_gui_acls(self, username: str, password: str | None):
|
||||||
|
# self._client.connector.send(
|
||||||
|
# MessageEndpoints.gui_acls(self._gui_id),
|
||||||
|
# messages.CredentialsMessage(
|
||||||
|
# credentials={
|
||||||
|
# "token": self._fernet.encrypt(
|
||||||
|
# msgpack.dumps({"username": username, "password": password})
|
||||||
|
# )
|
||||||
|
# }
|
||||||
|
# ),
|
||||||
|
# )
|
||||||
|
|
||||||
def start_server(self, wait=False) -> None:
|
def start_server(self, wait=False) -> None:
|
||||||
"""
|
"""
|
||||||
Start the GUI server, and execute callback when it is launched
|
Start the GUI server, and execute callback when it is launched
|
||||||
@ -271,8 +295,18 @@ class BECGuiClient(RPCBase):
|
|||||||
logger.success("GUI starting...")
|
logger.success("GUI starting...")
|
||||||
self._startup_timeout = 5
|
self._startup_timeout = 5
|
||||||
self._gui_started_event.clear()
|
self._gui_started_event.clear()
|
||||||
|
encr_token = Fernet.generate_key()
|
||||||
|
self._fernet = Fernet(encr_token)
|
||||||
|
conn = self._client.connector._redis_conn.connection_pool.connection_kwargs
|
||||||
|
acl_data = {"username": conn.get("username"), "password": conn.get("password")}
|
||||||
|
acl_data = self._fernet.encrypt(msgpack.dumps(acl_data))
|
||||||
self._process, self._process_output_processing_thread = _start_plot_process(
|
self._process, self._process_output_processing_thread = _start_plot_process(
|
||||||
self._gui_id, self.__class__, self._client._service_config.config, logger=logger
|
self._gui_id,
|
||||||
|
self.__class__,
|
||||||
|
self._client._service_config.config,
|
||||||
|
acl_data=acl_data,
|
||||||
|
token=encr_token,
|
||||||
|
logger=logger,
|
||||||
)
|
)
|
||||||
|
|
||||||
def gui_started_callback(callback):
|
def gui_started_callback(callback):
|
||||||
|
@ -2,16 +2,19 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import functools
|
import functools
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
from contextlib import contextmanager, redirect_stderr, redirect_stdout
|
from contextlib import contextmanager, redirect_stderr, redirect_stdout
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
import msgpack
|
||||||
from bec_lib.endpoints import MessageEndpoints
|
from bec_lib.endpoints import MessageEndpoints
|
||||||
from bec_lib.logger import bec_logger
|
from bec_lib.logger import bec_logger
|
||||||
from bec_lib.service_config import ServiceConfig
|
from bec_lib.service_config import ServiceConfig
|
||||||
from bec_lib.utils.import_utils import lazy_import
|
from bec_lib.utils.import_utils import lazy_import
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
from qtpy.QtCore import Qt, QTimer
|
from qtpy.QtCore import Qt, QTimer
|
||||||
from redis.exceptions import RedisError
|
from redis.exceptions import RedisError
|
||||||
|
|
||||||
@ -57,7 +60,10 @@ class BECWidgetsCLIServer:
|
|||||||
client=None,
|
client=None,
|
||||||
config=None,
|
config=None,
|
||||||
gui_class: Union[BECFigure, BECDockArea] = BECFigure,
|
gui_class: Union[BECFigure, BECDockArea] = BECFigure,
|
||||||
|
token: str = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
self._fernet = Fernet(token) if token else None
|
||||||
|
self._init_acls(config)
|
||||||
self.status = messages.BECStatus.BUSY
|
self.status = messages.BECStatus.BUSY
|
||||||
self.dispatcher = BECDispatcher(config=config) if dispatcher is None else dispatcher
|
self.dispatcher = BECDispatcher(config=config) if dispatcher is None else dispatcher
|
||||||
self.client = self.dispatcher.client if client is None else client
|
self.client = self.dispatcher.client if client is None else client
|
||||||
@ -67,6 +73,8 @@ class BECWidgetsCLIServer:
|
|||||||
self.rpc_register = RPCRegister()
|
self.rpc_register = RPCRegister()
|
||||||
self.rpc_register.add_rpc(self.gui)
|
self.rpc_register.add_rpc(self.gui)
|
||||||
|
|
||||||
|
# self.dispatcher.connect_slot(self.on_acl_update, MessageEndpoints.gui_acl(self.gui_id))
|
||||||
|
|
||||||
self.dispatcher.connect_slot(
|
self.dispatcher.connect_slot(
|
||||||
self.on_rpc_update, MessageEndpoints.gui_instructions(self.gui_id)
|
self.on_rpc_update, MessageEndpoints.gui_instructions(self.gui_id)
|
||||||
)
|
)
|
||||||
@ -79,6 +87,13 @@ class BECWidgetsCLIServer:
|
|||||||
self.status = messages.BECStatus.RUNNING
|
self.status = messages.BECStatus.RUNNING
|
||||||
logger.success(f"Server started with gui_id: {self.gui_id}")
|
logger.success(f"Server started with gui_id: {self.gui_id}")
|
||||||
|
|
||||||
|
def _init_acls(self, config: ServiceConfig):
|
||||||
|
acl_data = os.getenv("BEC_GUI_ACL")
|
||||||
|
if not acl_data:
|
||||||
|
return
|
||||||
|
acl_data = msgpack.loads(self._fernet.decrypt(acl_data))
|
||||||
|
config.config["acl"] = acl_data
|
||||||
|
|
||||||
def on_rpc_update(self, msg: dict, metadata: dict):
|
def on_rpc_update(self, msg: dict, metadata: dict):
|
||||||
request_id = metadata.get("request_id")
|
request_id = metadata.get("request_id")
|
||||||
logger.debug(f"Received RPC instruction: {msg}, metadata: {metadata}")
|
logger.debug(f"Received RPC instruction: {msg}, metadata: {metadata}")
|
||||||
@ -96,6 +111,9 @@ class BECWidgetsCLIServer:
|
|||||||
logger.debug(f"RPC instruction executed successfully: {res}")
|
logger.debug(f"RPC instruction executed successfully: {res}")
|
||||||
self.send_response(request_id, True, {"result": res})
|
self.send_response(request_id, True, {"result": res})
|
||||||
|
|
||||||
|
def on_acl_update(self, msg: dict, metadata: dict):
|
||||||
|
logger.debug(f"Received ACL update: {msg}, metadata: {metadata}")
|
||||||
|
|
||||||
def send_response(self, request_id: str, accepted: bool, msg: dict):
|
def send_response(self, request_id: str, accepted: bool, msg: dict):
|
||||||
self.client.connector.set_and_publish(
|
self.client.connector.set_and_publish(
|
||||||
MessageEndpoints.gui_instruction_response(request_id),
|
MessageEndpoints.gui_instruction_response(request_id),
|
||||||
@ -190,13 +208,10 @@ def _start_server(gui_id: str, gui_class: Union[BECFigure, BECDockArea], config:
|
|||||||
# if no config is provided, use the default config
|
# if no config is provided, use the default config
|
||||||
service_config = ServiceConfig()
|
service_config = ServiceConfig()
|
||||||
|
|
||||||
# bec_logger.configure(
|
token = os.getenv("BEC_GUI_TOKEN")
|
||||||
# service_config.redis,
|
server = BECWidgetsCLIServer(
|
||||||
# QtRedisConnector,
|
gui_id=gui_id, config=service_config, gui_class=gui_class, token=token
|
||||||
# service_name="BECWidgetsCLIServer",
|
)
|
||||||
# service_config=service_config.service_config,
|
|
||||||
# )
|
|
||||||
server = BECWidgetsCLIServer(gui_id=gui_id, config=service_config, gui_class=gui_class)
|
|
||||||
return server
|
return server
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ dependencies = [
|
|||||||
"pyte", # needed for vt100 console
|
"pyte", # needed for vt100 console
|
||||||
"qtconsole~=5.5, >=5.5.1", # needed for jupyter console
|
"qtconsole~=5.5, >=5.5.1", # needed for jupyter console
|
||||||
"qtpy~=2.4",
|
"qtpy~=2.4",
|
||||||
|
"cryptography~=44.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user