mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-14 12:17:02 +00:00
Add a persistent authentication secret to SerenaConfig
- Generate a random UUID when auth_secret is missing, null, or empty. - Persist generated secrets and preserve configured values across reloads. - Document the shared secret and test generation, persistence, and direct construction.
This commit is contained in:
1 parent
613f2ca098
commit
5a2063fc3a
4 files changed
+49
No files matched your search
@@ -12,6 +12,9 @@ Status of the `main` branch. Changes prior to the next official version change w
|
||||
see `CONTRIBUTING.md`
|
||||
|
||||
* General:
|
||||
- Add `auth_secret` to `serena_config.yml` for authenticating communication between Serena components
|
||||
and services. When missing, null, or empty, a random UUID is generated and persisted; existing values
|
||||
are preserved
|
||||
- Fix: MCP `initialize` now reports Serena's version instead of the installed mcp SDK version (#1889)
|
||||
- Fix: Parallel agents auto-registering projects could overwrite each other's changes to the global
|
||||
project list in `serena_config.yml`
|
||||
|
||||
@@ -16,6 +16,7 @@ from enum import Enum
|
||||
from functools import cached_property
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Optional, Self, TypeVar
|
||||
from uuid import uuid4
|
||||
|
||||
import yaml
|
||||
from ruamel.yaml.comments import CommentedMap
|
||||
@@ -945,6 +946,11 @@ class SerenaConfig(SharedConfig, ModeSelectionDefinitionWithBaseModes):
|
||||
# *** fields that are mapped directly to/from the configuration file (DO NOT RENAME) ***
|
||||
|
||||
projects: list[RegisteredProject] = field(default_factory=list)
|
||||
auth_secret: str = field(default_factory=lambda: str(uuid4()), repr=False)
|
||||
"""
|
||||
shared secret for authenticating communication between Serena components and services.
|
||||
A random UUID is generated and persisted when the configuration setting is missing or empty.
|
||||
"""
|
||||
gui_log_window: bool = False
|
||||
log_level: int = logging.INFO
|
||||
trace_lsp_communication: bool = False
|
||||
@@ -1138,6 +1144,11 @@ class SerenaConfig(SharedConfig, ModeSelectionDefinitionWithBaseModes):
|
||||
assert hasattr(instance, field_name)
|
||||
setattr(instance, field_name, get_value_or_default(field_name))
|
||||
|
||||
# generate a persistent authentication secret for explicitly unset settings
|
||||
if not instance.auth_secret:
|
||||
instance.auth_secret = str(uuid4())
|
||||
num_migrations += 1
|
||||
|
||||
# read projects
|
||||
if "projects" not in loaded_commented_yaml:
|
||||
raise SerenaConfigError("`projects` key not found in Serena configuration. Please update your `serena_config.yml` file.")
|
||||
|
||||
@@ -235,5 +235,9 @@ project_serena_folder_location: "$projectDir/.serena"
|
||||
# The pattern "**" matches any project path, so it can be used to trust all projects.
|
||||
trusted_project_path_patterns: []
|
||||
|
||||
# shared secret for authenticating communication between Serena components and services.
|
||||
# Keep this value private. When missing, null, or empty, a random UUID is generated and saved on load.
|
||||
auth_secret:
|
||||
|
||||
# the list of registered project paths (updated automatically).
|
||||
projects: []
|
||||
@@ -4,6 +4,7 @@ import shutil
|
||||
import tempfile
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
from uuid import UUID
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -635,6 +636,36 @@ class TestSerenaConfigLoadSave:
|
||||
config = SerenaConfig.from_config_file(generate_if_missing=False)
|
||||
assert config.projects == []
|
||||
|
||||
@pytest.mark.parametrize("setting", ["", "auth_secret: null\n", 'auth_secret: ""\n'])
|
||||
def test_unset_auth_secret_is_generated_and_persisted(self, setting: str) -> None:
|
||||
# load an existing configuration without a usable secret
|
||||
self.master_config_path.write_text("projects: []\n" + setting)
|
||||
config = SerenaConfig.from_config_file(generate_if_missing=False)
|
||||
|
||||
# subsequent loads retain the generated random UUID
|
||||
assert UUID(config.auth_secret).version == 4
|
||||
assert SerenaConfig.from_config_file(generate_if_missing=False).auth_secret == config.auth_secret
|
||||
|
||||
def test_configured_auth_secret_is_preserved(self) -> None:
|
||||
# retain a user-provided secret across loading and migration
|
||||
self.master_config_path.write_text("projects: []\nauth_secret: custom-secret\n")
|
||||
assert SerenaConfig.from_config_file(generate_if_missing=False).auth_secret == "custom-secret"
|
||||
assert SerenaConfig.from_config_file(generate_if_missing=False).auth_secret == "custom-secret"
|
||||
|
||||
def test_new_config_has_persistent_auth_secret(self) -> None:
|
||||
# generate the configuration from the template and retain its secret
|
||||
config = SerenaConfig.from_config_file()
|
||||
assert UUID(config.auth_secret).version == 4
|
||||
assert SerenaConfig.from_config_file().auth_secret == config.auth_secret
|
||||
|
||||
def test_direct_config_instances_have_distinct_auth_secrets(self) -> None:
|
||||
# directly constructed configurations receive independent secrets
|
||||
first = SerenaConfig()
|
||||
second = SerenaConfig()
|
||||
assert UUID(first.auth_secret).version == 4
|
||||
assert UUID(second.auth_secret).version == 4
|
||||
assert first.auth_secret != second.auth_secret
|
||||
|
||||
def test_malformed_project_is_skipped_with_warning(self, caplog):
|
||||
"""A malformed project.yml must not abort loading of the others."""
|
||||
good_project = self._make_project_dir(
|
||||
|
||||
Reference in new issue
Block a user