Coverage for presidio_analyzer / llm_utils / azure_auth_helper.py: 92%

24 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-03-29 09:03 +0000

1"""Azure authentication utilities for Presidio components.""" 

2 

3import logging 

4import os 

5from typing import TYPE_CHECKING, Optional 

6 

7if TYPE_CHECKING: 

8 from azure.core.credentials import TokenCredential 

9 

10try: 

11 from azure.identity import ( 

12 ChainedTokenCredential, 

13 DefaultAzureCredential, 

14 EnvironmentCredential, 

15 ManagedIdentityCredential, 

16 WorkloadIdentityCredential, 

17 ) 

18 AZURE_IDENTITY_AVAILABLE = True 

19except ImportError: # pragma: no cover 

20 AZURE_IDENTITY_AVAILABLE = False 

21 ChainedTokenCredential = None 

22 DefaultAzureCredential = None 

23 EnvironmentCredential = None 

24 ManagedIdentityCredential = None 

25 WorkloadIdentityCredential = None 

26 

27logger = logging.getLogger("presidio-analyzer") 

28 

29 

30def get_azure_credential() -> "TokenCredential": 

31 """ 

32 Get an Azure credential with environment-aware fallback strategy. 

33 

34 In development (ENV=development), uses DefaultAzureCredential which tries 

35 multiple authentication methods including Azure CLI, environment variables, 

36 and managed identity. 

37 

38 In production (ENV!=development), uses a more restrictive ChainedTokenCredential 

39 that only tries: 

40 1. EnvironmentCredential (service principal via env vars) 

41 2. WorkloadIdentityCredential (Kubernetes workload identity) 

42 3. ManagedIdentityCredential (Azure managed identity) 

43 

44 This production approach avoids interactive authentication methods and 

45 follows security best practices for production deployments. 

46 

47 :return: Configured Azure credential instance. 

48 :raises ImportError: If azure-identity is not installed. 

49 """ 

50 if not AZURE_IDENTITY_AVAILABLE: 

51 raise ImportError( 

52 "azure-identity is required for Azure authentication. " 

53 "Install it with: pip install azure-identity" 

54 ) 

55 

56 if os.getenv('ENV') == 'development': 

57 credential = DefaultAzureCredential() # CodeQL [SM05139] OK for dev 

58 logger.debug("Using DefaultAzureCredential (development mode)") 

59 return credential 

60 else: 

61 credential = ChainedTokenCredential( 

62 EnvironmentCredential(), 

63 WorkloadIdentityCredential(), 

64 ManagedIdentityCredential() 

65 ) 

66 logger.debug("Using ChainedTokenCredential (production mode)") 

67 return credential 

68 

69 

70def get_bearer_token_provider_for_scope( 

71 scope: str, 

72 credential: Optional["TokenCredential"] = None 

73): 

74 """ 

75 Get a bearer token provider function for a specific Azure scope. 

76 

77 This is commonly used with Azure OpenAI and other Azure Cognitive Services 

78 that require token-based authentication. 

79 

80 :param scope: Azure scope for the token (e.g., 

81 "https://cognitiveservices.azure.com/.default"). 

82 :param credential: Optional Azure credential. If not provided, uses 

83 get_azure_credential(). 

84 :return: Token provider function compatible with Azure SDK clients. 

85 :raises ImportError: If azure-identity is not installed. 

86 """ 

87 if not AZURE_IDENTITY_AVAILABLE: 

88 raise ImportError( 

89 "azure-identity is required for token provider. " 

90 "Install it with: pip install azure-identity" 

91 ) 

92 

93 from azure.identity import get_bearer_token_provider 

94 

95 if credential is None: 

96 credential = get_azure_credential() 

97 

98 return get_bearer_token_provider(credential, scope)