codekingpro/portable-devtools
114k
1# ------------------------------------2# Copyright (c) Microsoft Corporation.3# Licensed under the MIT License.4# ------------------------------------5import os6import logging7from contextvars import ContextVar8from string import ascii_letters, digits9from typing import List, Optional10 11from urllib.parse import urlparse12 13from azure.core.exceptions import ClientAuthenticationError14from .._constants import EnvironmentVariables, KnownAuthorities15 16within_credential_chain = ContextVar("within_credential_chain", default=False)17within_dac = ContextVar("within_dac", default=False)18 19_LOGGER = logging.getLogger(__name__)20 21VALID_TENANT_ID_CHARACTERS = frozenset(ascii_letters + digits + "-.")22VALID_SCOPE_CHARACTERS = frozenset(ascii_letters + digits + "_-.:/")23 24 25def normalize_authority(authority: str) -> str:26 """Ensure authority uses https, strip trailing spaces and /.27 28 :param str authority: authority to normalize29 :return: normalized authority30 :rtype: str31 :raises: ValueError if authority is not a valid https URL32 """33 34 parsed = urlparse(authority)35 if not parsed.scheme:36 return "https://" + authority.rstrip(" /")37 if parsed.scheme != "https":38 raise ValueError(39 "'{}' is an invalid authority. The value must be a TLS protected (https) URL.".format(authority)40 )41 42 return authority.rstrip(" /")43 44 45def get_default_authority() -> str:46 authority = os.environ.get(EnvironmentVariables.AZURE_AUTHORITY_HOST, KnownAuthorities.AZURE_PUBLIC_CLOUD)47 return normalize_authority(authority)48 49 50def validate_scope(scope: str) -> None:51 """Raise ValueError if scope is empty or contains a character invalid for a scope52 53 :param str scope: scope to validate54 :raises: ValueError if scope is empty or contains a character invalid for a scope.55 """56 if not scope or any(c not in VALID_SCOPE_CHARACTERS for c in scope):57 raise ValueError(58 "An invalid scope was provided. Only alphanumeric characters, '.', '-', '_', ':', and '/' are allowed."59 )60 61 62def validate_tenant_id(tenant_id: str) -> None:63 """Raise ValueError if tenant_id is empty or contains a character invalid for a tenant ID.64 65 :param str tenant_id: tenant ID to validate66 :raises: ValueError if tenant_id is empty or contains a character invalid for a tenant ID.67 """68 if not tenant_id or any(c not in VALID_TENANT_ID_CHARACTERS for c in tenant_id):69 raise ValueError(70 "Invalid tenant ID provided. You can locate your tenant ID by following the instructions here: "71 + "https://learn.microsoft.com/partner-center/find-ids-and-domain-names"72 )73 74 75def resolve_tenant(76 default_tenant: str, tenant_id: Optional[str] = None, *, additionally_allowed_tenants: List[str] = [], **_77) -> str:78 """Returns the correct tenant for a token request given a credential's configuration.79 80 :param str default_tenant: The tenant ID configured on the credential.81 :param str tenant_id: The tenant ID requested by the user.82 :keyword list[str] additionally_allowed_tenants: The list of additionally allowed tenants.83 :return: The tenant ID to use for the token request.84 :rtype: str85 :raises: ~azure.core.exceptions.ClientAuthenticationError86 """87 if tenant_id is None or tenant_id == default_tenant:88 return default_tenant89 if default_tenant == "adfs" or os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH):90 _LOGGER.info(91 "A token was request for a different tenant than was configured on the credential, "92 "but the configured value was used since multi tenant authentication has been disabled. "93 "Configured tenant ID: %s, Requested tenant ID %s",94 default_tenant,95 tenant_id,96 )97 return default_tenant98 if not default_tenant:99 return tenant_id100 if "*" in additionally_allowed_tenants or tenant_id in additionally_allowed_tenants:101 _LOGGER.info(102 "A token was requested for a different tenant than was configured on the credential, "103 "and the requested tenant ID was used to authenticate. Configured tenant ID: %s, "104 "Requested tenant ID %s",105 default_tenant,106 tenant_id,107 )108 return tenant_id109 raise ClientAuthenticationError(110 message="The current credential is not configured to acquire tokens for tenant {}. "111 "To enable acquiring tokens for this tenant add it to the additionally_allowed_tenants "112 'when creating the credential, or add "*" to additionally_allowed_tenants to allow '113 "acquiring tokens for any tenant.".format(tenant_id)114 )115 