diff --git a/pydantic_settings/sources/providers/azure.py b/pydantic_settings/sources/providers/azure.py index 94d9f936..c949125a 100644 --- a/pydantic_settings/sources/providers/azure.py +++ b/pydantic_settings/sources/providers/azure.py @@ -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: @@ -68,7 +68,6 @@ def __iter__(self) -> Iterator[str]: class AzureKeyVaultSettingsSource(EnvSettingsSource): _url: str _credential: TokenCredential - _secret_client: SecretClient def __init__( self, diff --git a/tests/test_source_azure_key_vault.py b/tests/test_source_azure_key_vault.py index 075ce0cc..934d9a5b 100644 --- a/tests/test_source_azure_key_vault.py +++ b/tests/test_source_azure_key_vault.py @@ -3,6 +3,7 @@ """ import pytest +from azure.keyvault.secrets import SecretClient from pydantic import BaseModel, Field from pytest_mock import MockerFixture @@ -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.""" @@ -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() @@ -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( @@ -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.""" @@ -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, )