Skip to content

Commit 8b12ebe

Browse files
authored
Validate tenant IDs used in URLs (Azure#14955)
1 parent c0eab07 commit 8b12ebe

23 files changed

+187
-18
lines changed

sdk/identity/azure-identity/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
provided, the credential will authenticate users to an Azure development
1111
application.
1212
([#14354](https://github.com/Azure/azure-sdk-for-python/issues/14354))
13+
- Credentials raise `ValueError` when constructed with tenant IDs containing
14+
invalid characters
15+
([#14821](https://github.com/Azure/azure-sdk-for-python/issues/14821))
1316
- Raised minimum msal version to 1.6.0
1417

1518
### Fixed

sdk/identity/azure-identity/azure/identity/_credentials/certificate.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from cryptography.hazmat.backends import default_backend
1111
import six
1212

13+
from .._internal import validate_tenant_id
1314
from .._internal.client_credential_base import ClientCredentialBase
1415

1516
if TYPE_CHECKING:
@@ -40,6 +41,7 @@ class CertificateCredential(ClientCredentialBase):
4041

4142
def __init__(self, tenant_id, client_id, certificate_path, **kwargs):
4243
# type: (str, str, str, **Any) -> None
44+
validate_tenant_id(tenant_id)
4345
if not certificate_path:
4446
raise ValueError(
4547
"'certificate_path' must be the path to a PEM file containing an x509 certificate and its private key"

sdk/identity/azure-identity/azure/identity/_credentials/shared_cache.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from .. import CredentialUnavailableError
1313
from .._constants import DEVELOPER_SIGN_ON_CLIENT_ID
14-
from .._internal import AadClient
14+
from .._internal import AadClient, validate_tenant_id
1515
from .._internal.decorators import log_get_token, wrap_exceptions
1616
from .._internal.msal_client import MsalClient
1717
from .._internal.shared_token_cache import NO_TOKEN, SharedTokenCacheBase
@@ -53,6 +53,7 @@ def __init__(self, username=None, **kwargs):
5353
if self._auth_record:
5454
# authenticate in the tenant that produced the record unless "tenant_id" specifies another
5555
self._tenant_id = kwargs.pop("tenant_id", None) or self._auth_record.tenant_id
56+
validate_tenant_id(self._tenant_id)
5657
self._cache = kwargs.pop("_cache", None)
5758
self._app = None
5859
self._client_kwargs = kwargs

sdk/identity/azure-identity/azure/identity/_credentials/vscode.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from .._exceptions import CredentialUnavailableError
99
from .._constants import AZURE_VSCODE_CLIENT_ID
10+
from .._internal import validate_tenant_id
1011
from .._internal.aad_client import AadClient
1112
from .._internal.decorators import log_get_token
1213

@@ -38,6 +39,7 @@ def __init__(self, **kwargs):
3839
self._refresh_token = None
3940
self._client = kwargs.pop("_client", None)
4041
self._tenant_id = kwargs.pop("tenant_id", None) or "organizations"
42+
validate_tenant_id(self._tenant_id)
4143
if not self._client:
4244
self._client = AadClient(self._tenant_id, AZURE_VSCODE_CLIENT_ID, **kwargs)
4345

sdk/identity/azure-identity/azure/identity/_internal/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ def get_default_authority():
2929
return normalize_authority(authority)
3030

3131

32+
VALID_TENANT_ID_CHARACTERS = frozenset("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "-.")
33+
34+
35+
def validate_tenant_id(tenant_id):
36+
"""Raise ValueError if tenant_id is empty or contains a character invalid for a tenant id"""
37+
# type: (str) -> None
38+
if not tenant_id or any(c not in VALID_TENANT_ID_CHARACTERS for c in tenant_id):
39+
raise ValueError(
40+
"Invalid tenant id provided. You can locate your tenant id by following the instructions here: "
41+
+ "https://docs.microsoft.com/partner-center/find-ids-and-domain-names"
42+
)
43+
44+
3245
# pylint:disable=wrong-import-position
3346
from .aad_client import AadClient
3447
from .aad_client_base import AadClientBase

sdk/identity/azure-identity/azure/identity/_internal/certificate_credential_base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from . import AadClientCertificate
1111
from .persistent_cache import load_service_principal_cache
12+
from .._internal import validate_tenant_id
1213

1314
try:
1415
ABC = abc.ABC
@@ -28,6 +29,7 @@
2829
class CertificateCredentialBase(ABC):
2930
def __init__(self, tenant_id, client_id, certificate_path, **kwargs):
3031
# type: (str, str, str, **Any) -> None
32+
validate_tenant_id(tenant_id)
3133
if not certificate_path:
3234
raise ValueError(
3335
"'certificate_path' must be the path to a PEM file containing an x509 certificate and its private key"

sdk/identity/azure-identity/azure/identity/_internal/client_secret_credential_base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from msal import TokenCache
99

10+
from . import validate_tenant_id
1011
from .persistent_cache import load_service_principal_cache
1112

1213
try:
@@ -30,6 +31,7 @@ def __init__(self, tenant_id, client_id, client_secret, **kwargs):
3031
raise ValueError(
3132
"tenant_id should be an Azure Active Directory tenant's id (also called its 'directory id')"
3233
)
34+
validate_tenant_id(tenant_id)
3335

3436
enable_persistent_cache = kwargs.pop("enable_persistent_cache", False)
3537
if enable_persistent_cache:

sdk/identity/azure-identity/azure/identity/_internal/msal_credentials.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
import abc
66

77
import msal
8-
from azure.core.credentials import AccessToken
98

109
from .msal_client import MsalClient
1110
from .persistent_cache import load_user_cache
12-
from .._internal import get_default_authority, normalize_authority
11+
from .._internal import get_default_authority, normalize_authority, validate_tenant_id
1312

1413
try:
1514
ABC = abc.ABC
@@ -34,6 +33,7 @@ def __init__(self, client_id, client_credential=None, **kwargs):
3433
authority = kwargs.pop("authority", None)
3534
self._authority = normalize_authority(authority) if authority else get_default_authority()
3635
self._tenant_id = kwargs.pop("tenant_id", None) or "organizations"
36+
validate_tenant_id(self._tenant_id)
3737

3838
self._client_credential = client_credential
3939
self._client_id = client_id

sdk/identity/azure-identity/azure/identity/aio/_credentials/vscode.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from .._internal.aad_client import AadClient
1111
from .._internal.decorators import log_get_token_async
1212
from ..._credentials.vscode import get_credentials
13+
from ..._internal import validate_tenant_id
1314

1415
if TYPE_CHECKING:
1516
# pylint:disable=unused-import,ungrouped-imports
@@ -31,6 +32,7 @@ def __init__(self, **kwargs: "Any") -> None:
3132
self._refresh_token = None
3233
self._client = kwargs.pop("_client", None)
3334
self._tenant_id = kwargs.pop("tenant_id", None) or "organizations"
35+
validate_tenant_id(self._tenant_id)
3436
if not self._client:
3537
self._client = AadClient(self._tenant_id, AZURE_VSCODE_CLIENT_ID, **kwargs)
3638

sdk/identity/azure-identity/tests/test_aad_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def assert_secrets_not_exposed():
7575

7676
@pytest.mark.parametrize("authority", ("localhost", "https://localhost"))
7777
def test_request_url(authority):
78-
tenant_id = "expected_tenant"
78+
tenant_id = "expected-tenant"
7979
parsed_authority = urlparse(authority)
8080
expected_netloc = parsed_authority.netloc or authority # "localhost" parses to netloc "", path "localhost"
8181

0 commit comments

Comments
 (0)