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
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-29 09:03 +0000
1"""Azure authentication utilities for Presidio components."""
3import logging
4import os
5from typing import TYPE_CHECKING, Optional
7if TYPE_CHECKING:
8 from azure.core.credentials import TokenCredential
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
27logger = logging.getLogger("presidio-analyzer")
30def get_azure_credential() -> "TokenCredential":
31 """
32 Get an Azure credential with environment-aware fallback strategy.
34 In development (ENV=development), uses DefaultAzureCredential which tries
35 multiple authentication methods including Azure CLI, environment variables,
36 and managed identity.
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)
44 This production approach avoids interactive authentication methods and
45 follows security best practices for production deployments.
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 )
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
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.
77 This is commonly used with Azure OpenAI and other Azure Cognitive Services
78 that require token-based authentication.
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 )
93 from azure.identity import get_bearer_token_provider
95 if credential is None:
96 credential = get_azure_credential()
98 return get_bearer_token_provider(credential, scope)