Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/ess/livedata/core/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def _stop_impl(self) -> None:

@staticmethod
def setup_arg_parser(
description: str, *, dev_flag: bool = True
description: str, *, dev_flag: bool = True, dashboard_auth: bool = False
) -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description=description,
Expand All @@ -201,6 +201,17 @@ def setup_arg_parser(
default='INFO',
help='Set the logging level',
)
if dashboard_auth:
parser.add_argument(
'--basic-auth-password',
default=None,
help='Basic authentication password for the dashboard',
Copy link

Copilot AI Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Command-line arguments for passwords can be visible in process lists and shell history. Consider adding a warning in the help text to recommend using the environment variable LIVEDATA_BASIC_AUTH_PASSWORD instead for better security.

Suggested change
help='Basic authentication password for the dashboard',
help='Basic authentication password for the dashboard. '
'WARNING: Command-line arguments can be visible in process lists and shell history. '
'For better security, use the environment variable LIVEDATA_BASIC_AUTH_PASSWORD instead.',

Copilot uses AI. Check for mistakes.
)
parser.add_argument(
'--basic-auth-cookie-secret',
default=None,
help='Basic authentication cookie secret for the dashboard',
)
return parser


Expand All @@ -218,7 +229,6 @@ def get_env_defaults(
# Convert --arg-name to LIVEDATA_ARG_NAME
env_name = f"{prefix}_{action.dest.upper().replace('-', '_')}"
env_val = os.getenv(env_name)

# Override with environment variable if present
if env_val is not None:
if isinstance(default_value, bool):
Expand Down
9 changes: 8 additions & 1 deletion src/ess/livedata/dashboard/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,16 @@ def __init__(
log_level: int = logging.INFO,
dashboard_name: str,
port: int = 5007,
basic_auth_password: str | None = None,
basic_auth_cookie_secret: str | None = None,
):
name = f'{instrument}_{dashboard_name}'
super().__init__(name=name, log_level=log_level)
self._instrument = instrument
self._port = port
self._dev = dev

self._basic_auth_password = basic_auth_password
self._basic_auth_cookie_secret = basic_auth_cookie_secret
self._exit_stack = ExitStack()
self._exit_stack.__enter__()

Expand Down Expand Up @@ -258,6 +261,8 @@ def server(self):
show=False,
autoreload=False,
dev=self._dev,
basic_auth=self._basic_auth_password,
cookie_secret=self._basic_auth_cookie_secret,
Comment on lines +264 to +265
Copy link

Copilot AI Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basic authentication credentials are being passed directly to the panel server. Consider validating that both basic_auth_password and basic_auth_cookie_secret are provided together, as partial authentication configuration could lead to unexpected behavior or security issues.

Copilot uses AI. Check for mistakes.
)

def _start_impl(self) -> None:
Expand All @@ -276,6 +281,8 @@ def run_forever(self) -> None:
show=False,
autoreload=True,
dev=self._dev,
basic_auth=self._basic_auth_password,
cookie_secret=self._basic_auth_cookie_secret,
)
except KeyboardInterrupt:
self._logger.info("Keyboard interrupt received, shutting down...")
Expand Down
16 changes: 14 additions & 2 deletions src/ess/livedata/dashboard/reduction.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,23 @@
class ReductionApp(DashboardBase):
"""Reduction dashboard application."""

def __init__(self, *, instrument: str = 'dummy', dev: bool = False, log_level: int):
def __init__(
self,
*,
instrument: str = 'dummy',
dev: bool = False,
log_level: int,
basic_auth_password: str | None = None,
basic_auth_cookie_secret: str | None = None,
):
super().__init__(
instrument=instrument,
dev=dev,
log_level=log_level,
dashboard_name='reduction_dashboard',
port=5009, # Default port for reduction dashboard
basic_auth_password=basic_auth_password,
basic_auth_cookie_secret=basic_auth_cookie_secret,
)
self._logger.info("Reduction dashboard initialized")

Expand All @@ -39,7 +49,9 @@ def create_main_content(self) -> pn.viewable.Viewable:


def get_arg_parser() -> argparse.ArgumentParser:
return Service.setup_arg_parser(description='ESSlivedata Dashboard')
return Service.setup_arg_parser(
description='ESSlivedata Dashboard', dashboard_auth=True
)
Comment on lines 51 to +54
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since auth is only for the dashboard, it might make more sense to add the options here before returning, instead of adding a flag to the to the generic Service, which is used by the backend workers as well?



def main() -> None:
Expand Down
Loading