def get_azure_token(): credential_chain = ChainedTokenCredential(ManagedIdentityCredential(), AzureCliCredential()) try: token = credential_chain.get_token("https://monitoring.azure.com//.default") return token.token except Exception as e: logging.exception(f"Failed to retrieve Azure token. Reason is {type(e).__name__} {e}") return None
def test_chain_returns_first_token(): expected_token = Mock() first_credential = Mock(get_token=lambda _: expected_token) second_credential = Mock(get_token=Mock()) aggregate = ChainedTokenCredential(first_credential, second_credential) credential = aggregate.get_token("scope") assert credential is expected_token assert second_credential.get_token.call_count == 0
def test_close(): credentials = [MagicMock(close=Mock()) for _ in range(5)] chain = ChainedTokenCredential(*credentials) for credential in credentials: assert credential.__exit__.call_count == 0 chain.close() for credential in credentials: assert credential.__exit__.call_count == 1
def __init__(self, repo, config): super().__init__(repo, config) self._account_url = None url = config.get("url", "azure://") self.path_info = self.PATH_CLS(url) self.bucket = self.path_info.bucket if not self.bucket: container = self._az_config.get("storage", "container_name", None) self.path_info = self.PATH_CLS(f"azure://{container}") self.bucket = self.path_info.bucket self._conn_str = config.get( "connection_string") or self._az_config.get( "storage", "connection_string", None) if not self._conn_str: name = config.get("storage_account") or self._az_config.get( "storage", "account", None) self._account_url = f"https://{name}.blob.core.windows.net" # Microsoft azure docs # https://docs.microsoft.com/en-us/python/api/overview/azure/identity-readme?view=azure-python self._credential = (config.get("sas_token") or config.get("storage_key") or self._az_config.get("storage", "key", None) or self._az_config.get("storage", "sas_token", None) or ChainedTokenCredential( ServicePrincipalConfigCredential(config), DefaultAzureCredential(), ))
def __init__(self, synapse_dev_url: str, pool_name: str, datalake_dir: str, executor_size: str, executors: int): tenant_id = '72f988bf-86f1-41af-91ab-2d7cd011db47' authority_host_uri = 'login.microsoftonline.com' client_id = '04b07795-8ddb-461a-bbee-02f9e1bf7b46' global login_credential_cache # use a global cache to store the credential, to avoid users from multiple login if login_credential_cache is None: # use DeviceCodeCredential if EnvironmentCredential is not available self.credential = ChainedTokenCredential( EnvironmentCredential(), DeviceCodeCredential(client_id, authority=authority_host_uri, tenant=tenant_id)) login_credential_cache = self.credential else: self.credential = login_credential_cache self._api = SynapseJobRunner(synapse_dev_url, pool_name, executor_size=executor_size, executors=executors, credential=self.credential) self._datalake = DataLakeFiler(datalake_dir, credential=self.credential)
def _default_chained_credentials(self) -> ChainedTokenCredential: managed_identity = ManagedIdentityCredential() azure_cli = AzureCliCredential() environment = EnvironmentCredential() shared_token_cache = SharedTokenCacheCredential() interactive_browser = InteractiveBrowserCredential() return ChainedTokenCredential(managed_identity, azure_cli, environment, shared_token_cache, interactive_browser)
def test_chain_attempts_all_credentials(): expected_token = AccessToken("expected_token", 0) credentials = [ Mock(get_token=Mock(side_effect=CredentialUnavailableError(message=""))), Mock(get_token=Mock(side_effect=CredentialUnavailableError(message=""))), Mock(get_token=Mock(return_value=expected_token)), ] token = ChainedTokenCredential(*credentials).get_token("scope") assert token is expected_token for credential in credentials: assert credential.get_token.call_count == 1
def test_credential_chain_error_message(): def raise_authn_error(message): raise ClientAuthenticationError(message) first_error = "first_error" first_credential = Mock(spec=ClientSecretCredential, get_token=lambda _: raise_authn_error(first_error)) second_error = "second_error" second_credential = Mock(name="second_credential", get_token=lambda _: raise_authn_error(second_error)) with pytest.raises(ClientAuthenticationError) as ex: ChainedTokenCredential(first_credential, second_credential).get_token("scope") assert "ClientSecretCredential" in ex.value.message assert first_error in ex.value.message assert second_error in ex.value.message
def test_chain_raises_for_unexpected_error(): """the chain should not continue after an unexpected error (i.e. anything but CredentialUnavailableError)""" expected_message = "it can't be done" credentials = [ Mock(get_token=Mock(side_effect=CredentialUnavailableError(message=""))), Mock(get_token=Mock(side_effect=ValueError(expected_message))), Mock(get_token=Mock(return_value=AccessToken("**", 42))), ] with pytest.raises(ClientAuthenticationError) as ex: ChainedTokenCredential(*credentials).get_token("scope") assert expected_message in ex.value.message assert credentials[-1].get_token.call_count == 0
def test_credential_chain_error_message(): first_error = "first_error" first_credential = Mock( spec=ClientSecretCredential, get_token=Mock(side_effect=CredentialUnavailableError(first_error)) ) second_error = "second_error" second_credential = Mock( name="second_credential", get_token=Mock(side_effect=ClientAuthenticationError(second_error)) ) with pytest.raises(ClientAuthenticationError) as ex: ChainedTokenCredential(first_credential, second_credential).get_token("scope") assert "ClientSecretCredential" in ex.value.message assert first_error in ex.value.message assert second_error in ex.value.message
def test_context_manager(): credentials = [MagicMock() for _ in range(5)] chain = ChainedTokenCredential(*credentials) for credential in credentials: assert credential.__enter__.call_count == 0 assert credential.__exit__.call_count == 0 with chain: for credential in credentials: assert credential.__enter__.call_count == 1 assert credential.__exit__.call_count == 0 for credential in credentials: assert credential.__enter__.call_count == 1 assert credential.__exit__.call_count == 1
def test_chain_attempts_all_credentials(): def raise_authn_error(message="it didn't work"): raise ClientAuthenticationError(message) expected_token = AccessToken("expected_token", 0) credentials = [ Mock(get_token=Mock(wraps=raise_authn_error)), Mock(get_token=Mock(wraps=raise_authn_error)), Mock(get_token=Mock(return_value=expected_token)), ] token = ChainedTokenCredential(*credentials).get_token("scope") assert token is expected_token for credential in credentials: assert credential.get_token.call_count == 1
) from azure.storage.queue import QueueServiceClient from azure.ai.formrecognizer import FormRecognizerClient from azure.ai.textanalytics import TextAnalyticsClient from azure.cosmos import CosmosClient from smart_getenv import getenv from dotmap import DotMap from dotenv import load_dotenv, find_dotenv load_dotenv(find_dotenv()) sleep = getenv("AZURE_STORAGE_QUEUE_RECEIVE_SLEEP", type=bool, default=1) credential = ChainedTokenCredential(AzureCliCredential(), EnvironmentCredential(), ManagedIdentityCredential()) queue_service_client = QueueServiceClient( account_url=getenv("AZURE_STORAGE_QUEUE_ENDPOINT"), credential=credential) queue_client = queue_service_client.get_queue_client( queue=getenv("AZURE_STORAGE_QUEUE_NAME", default="messages")) fr_client = FormRecognizerClient( endpoint=getenv("AZURE_FORM_RECOGNIZER_ENDPOINT"), credential=credential) ta_client = TextAnalyticsClient( endpoint=getenv("AZURE_TEXT_ANALYTICS_ENDPOINT"), credential=credential) cosmos_client = CosmosClient(url=getenv("AZURE_COSMOS_ENDPOINT"),
from azure.core.exceptions import HttpResponseError # Run run.get.secret.ps1 to initialize environment variables and run the app """ Manual: In Windows, using PowerShell, you can get and set temporary environment vars thusly: set: $env:VAULT_URL="https://keyvault-pythonqs-kv.vault.azure.net/" get: $env:VAULT_URL In Linux: set: export VAULT_URL="https://keyvault-pythonqs-kv.vault.azure.net/" get: echo $VAULT_URL """ VAULT_URL = os.environ["VAULT_URL"] SECRET_NAME = os.environ["SECRET_NAME"] # credential = DefaultAzureCredential() # This seems to work better: # https://docs.microsoft.com/en-us/answers/questions/74848/access-denied-to-first-party-service.html credential = ChainedTokenCredential(AzureCliCredential()) client = SecretClient(vault_url=VAULT_URL, credential=credential) try: # Let's get the secret using its name print("\n.. Get a Secret by name") the_secret = client.get_secret(SECRET_NAME) print("Secret with name '{0}' was found with value '{1}'.".format( the_secret.name, the_secret.value)) except HttpResponseError as e: print("\nThis sample has caught an error. {0}".format(e.message))
def az_connect_core(auth_methods: List[str] = None, silent: bool = False) -> AzCredentials: """ Authenticate using multiple authentication sources. Parameters ---------- auth_methods List of authentication methods to try Possible options are: - "env" - to get authentication details from environment varibales - "cli" - to use Azure CLI authentication details - "msi" - to user Managed Service Indenity details - "interactive" - to prompt for interactive login Default is ["env", "cli", "msi", "interactive"] silent Whether to display any output during auth process. Default is False. Returns ------- AzCredentials Named tuple of: - legacy (ADAL) credentials - modern (MSAL) credentials Raises ------ CloudError If chained token credential creation fails. MsticpyAzureConnectionError If invalid auth options are presented. Notes ----- The function tries to obtain credentials from the following sources: - Azure Auth Environment variables - Azure CLI (if an active session is logged on) - Managed Service Identity - Interactive browser logon If the authentication is successful both ADAL (legacy) and MSAL (modern) credential types are returned. """ if not auth_methods: auth_methods = default_auth_methods() try: auths = [_AUTH_OPTIONS[meth] for meth in auth_methods] except KeyError as err: raise MsticpyAzureConnectionError( "Unknown authentication option, valid options are; env, cli, msi, interactive" ) from err # Filter and replace error message when credentials not found handler = logging.StreamHandler(sys.stdout) if silent: handler.addFilter(_filter_all_warnings) else: handler.addFilter(_filter_credential_warning) logging.basicConfig(level=logging.WARNING, handlers=[handler]) # Create credentials and connect to the subscription client to validate creds = ChainedTokenCredential(*auths) legacy_creds = CredentialWrapper(creds) if not creds: raise CloudError("Could not obtain credentials.") return AzCredentials(legacy_creds, creds)