Merge sensitive fields from env vars when no DB record exists

This commit is contained in:
Ryan Crabbe 2026-03-04 17:37:11 -08:00
parent 95e64acd99
commit 149d136ab7
2 changed files with 58 additions and 1 deletions

View file

@ -178,7 +178,7 @@ async def update_hashicorp_vault_config(
config_data = config.model_dump(exclude_none=True)
# Merge with existing DB record: preserve sensitive fields the user didn't re-enter
# Merge sensitive fields the user didn't re-enter: try DB first, fall back to env vars
existing_record = await prisma_client.db.litellm_configoverrides.find_unique(
where={"config_type": "hashicorp_vault"}
)
@ -190,6 +190,12 @@ async def update_hashicorp_vault_config(
for field in HASHICORP_SENSITIVE_FIELDS:
if field not in config_data and existing_decrypted.get(field):
config_data[field] = existing_decrypted[field]
else:
# No DB record yet — merge from current env vars
env_values = _get_current_env_values(HASHICORP_ENV_VAR_MAPPING)
for field in HASHICORP_SENSITIVE_FIELDS:
if field not in config_data and env_values.get(field):
config_data[field] = env_values[field]
# Validate that the config has enough fields to initialize
has_vault_addr = bool(config_data.get("vault_addr"))

View file

@ -348,6 +348,57 @@ async def test_update_hashicorp_config_preserves_existing_sensitive_fields(
os.environ.pop("HCP_VAULT_APPROLE_ROLE_ID", None)
@pytest.mark.asyncio
async def test_update_hashicorp_config_merges_env_vars_when_no_db_record(
client, monkeypatch
):
"""POST without sensitive fields and no DB record should merge from env vars."""
mock_configoverrides = MagicMock()
mock_configoverrides.find_unique = AsyncMock(return_value=None)
mock_configoverrides.upsert = AsyncMock(return_value=None)
mock_prisma = MagicMock()
mock_prisma.db = MagicMock()
mock_prisma.db.litellm_configoverrides = mock_configoverrides
mock_proxy_config = MagicMock()
mock_proxy_config.initialize_secret_manager = MagicMock()
mock_proxy_config._last_hashicorp_vault_config = None
monkeypatch.setattr(ps, "prisma_client", mock_prisma)
monkeypatch.setattr(ps, "proxy_config", mock_proxy_config)
# Simulate existing env var token (set before UI was used)
monkeypatch.setenv("HCP_VAULT_TOKEN", "env-token")
with patch(
"litellm.proxy.management_endpoints.config_override_endpoints.encrypt_value_helper"
) as mock_encrypt:
mock_encrypt.side_effect = lambda v: f"enc_{v}"
app.dependency_overrides[ps.user_api_key_auth] = lambda: UserAPIKeyAuth(
user_role=LitellmUserRoles.PROXY_ADMIN, user_id="admin_user"
)
try:
# Only send vault_addr — token should merge from env var
response = client.post(
"/config_overrides/hashicorp_vault",
json={"vault_addr": "https://vault.new.com"},
)
assert response.status_code == 200
upsert_call = mock_configoverrides.upsert.call_args
create_data = json.loads(
upsert_call.kwargs["data"]["create"]["config_value"]
)
assert create_data["vault_addr"] == "https://vault.new.com"
assert create_data["vault_token"] == "enc_env-token"
finally:
app.dependency_overrides.pop(ps.user_api_key_auth, None)
os.environ.pop("HCP_VAULT_ADDR", None)
@pytest.mark.asyncio
async def test_update_hashicorp_config_init_failure_restores_env_vars(
client, monkeypatch