Skip to content
Merged
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
5 changes: 2 additions & 3 deletions pydantic_settings/sources/providers/azure.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ def __init__(
self._loaded_secrets = {}
self._secret_client = secret_client
self._secret_names: list[str] = [
secret.name for secret in self._secret_client.list_properties_of_secrets() if secret.name
secret.name for secret in self._secret_client.list_properties_of_secrets() if secret.name and secret.enabled
]

def __getitem__(self, key: str) -> str | None:
if key not in self._loaded_secrets:
if key not in self._loaded_secrets and key in self._secret_names:
try:
self._loaded_secrets[key] = self._secret_client.get_secret(key).value
except Exception:
Expand All @@ -68,7 +68,6 @@ def __iter__(self) -> Iterator[str]:
class AzureKeyVaultSettingsSource(EnvSettingsSource):
_url: str
_credential: TokenCredential
_secret_client: SecretClient

def __init__(
self,
Expand Down
60 changes: 50 additions & 10 deletions tests/test_source_azure_key_vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import pytest
from azure.keyvault.secrets import SecretClient
from pydantic import BaseModel, Field
from pytest_mock import MockerFixture

Expand All @@ -23,9 +24,6 @@
azure_key_vault = False


MODULE = 'pydantic_settings.sources.providers.azure'


@pytest.mark.skipif(not azure_key_vault, reason='pydantic-settings[azure-key-vault] is not installed')
class TestAzureKeyVaultSettingsSource:
"""Test AzureKeyVaultSettingsSource."""
Expand All @@ -36,7 +34,10 @@ def test___init__(self, mocker: MockerFixture) -> None:
class AzureKeyVaultSettings(BaseSettings):
"""AzureKeyVault settings."""

mocker.patch(f'{MODULE}.SecretClient.list_properties_of_secrets', return_value=[])
mocker.patch(
f'{AzureKeyVaultSettingsSource.__module__}.{SecretClient.list_properties_of_secrets.__qualname__}',
return_value=[],
)

AzureKeyVaultSettingsSource(
AzureKeyVaultSettings, 'https://my-resource.vault.azure.net/', DefaultAzureCredential()
Expand All @@ -55,11 +56,17 @@ class AzureKeyVaultSettings(BaseSettings):
sql_server_user: str = Field(..., alias='SqlServerUser')
sql_server: SqlServer = Field(..., alias='SqlServer')

expected_secrets = [type('', (), {'name': 'SqlServerUser'}), type('', (), {'name': 'SqlServer--Password'})]
expected_secrets = [
type('', (), {'name': 'SqlServerUser', 'enabled': True}),
type('', (), {'name': 'SqlServer--Password', 'enabled': True}),
]
expected_secret_value = 'SecretValue'
mocker.patch(f'{MODULE}.SecretClient.list_properties_of_secrets', return_value=expected_secrets)
mocker.patch(
f'{MODULE}.SecretClient.get_secret',
f'{AzureKeyVaultSettingsSource.__module__}.{SecretClient.list_properties_of_secrets.__qualname__}',
return_value=expected_secrets,
)
mocker.patch(
f'{AzureKeyVaultSettingsSource.__module__}.{SecretClient.get_secret.__qualname__}',
side_effect=self._raise_resource_not_found_when_getting_parent_secret_name,
)
obj = AzureKeyVaultSettingsSource(
Expand All @@ -71,6 +78,33 @@ class AzureKeyVaultSettings(BaseSettings):
assert settings['SqlServerUser'] == expected_secret_value
assert settings['SqlServer']['Password'] == expected_secret_value

def test_do_not_load_disabled_secrets(self, mocker: MockerFixture) -> None:
class AzureKeyVaultSettings(BaseSettings):
"""AzureKeyVault settings."""

SqlServerPassword: str
DisabledSqlServerPassword: str

disabled_secret_name = 'SqlServerPassword'
expected_secrets = [
type('', (), {'name': disabled_secret_name, 'enabled': False}),
]
mocker.patch(
f'{AzureKeyVaultSettingsSource.__module__}.{SecretClient.list_properties_of_secrets.__qualname__}',
return_value=expected_secrets,
)
mocker.patch(
f'{AzureKeyVaultSettingsSource.__module__}.{SecretClient.get_secret.__qualname__}',
return_value=KeyVaultSecret(SecretProperties(), 'SecretValue'),
)
obj = AzureKeyVaultSettingsSource(
AzureKeyVaultSettings, 'https://my-resource.vault.azure.net/', DefaultAzureCredential()
)

settings = obj()

assert disabled_secret_name not in settings

def test_azure_key_vault_settings_source(self, mocker: MockerFixture) -> None:
"""Test AzureKeyVaultSettingsSource."""

Expand Down Expand Up @@ -99,11 +133,17 @@ def settings_customise_sources(
),
)

expected_secrets = [type('', (), {'name': 'SqlServerUser'}), type('', (), {'name': 'SqlServer--Password'})]
expected_secrets = [
type('', (), {'name': 'SqlServerUser', 'enabled': True}),
type('', (), {'name': 'SqlServer--Password', 'enabled': True}),
]
expected_secret_value = 'SecretValue'
mocker.patch(f'{MODULE}.SecretClient.list_properties_of_secrets', return_value=expected_secrets)
mocker.patch(
f'{MODULE}.SecretClient.get_secret',
f'{AzureKeyVaultSettingsSource.__module__}.{SecretClient.list_properties_of_secrets.__qualname__}',
return_value=expected_secrets,
)
mocker.patch(
f'{AzureKeyVaultSettingsSource.__module__}.{SecretClient.get_secret.__qualname__}',
side_effect=self._raise_resource_not_found_when_getting_parent_secret_name,
)

Expand Down