py2docfx 0.1.20rc2195074__py3-none-any.whl → 0.1.20rc2245107__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py2docfx/venv/basevenv/Lib/site-packages/charset_normalizer/api.py +3 -2
- py2docfx/venv/basevenv/Lib/site-packages/charset_normalizer/legacy.py +17 -1
- py2docfx/venv/basevenv/Lib/site-packages/charset_normalizer/version.py +1 -1
- py2docfx/venv/basevenv/Lib/site-packages/requests/__version__.py +2 -2
- py2docfx/venv/basevenv/Lib/site-packages/requests/adapters.py +17 -40
- py2docfx/venv/basevenv/Lib/site-packages/requests/sessions.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/core/_version.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/core/pipeline/policies/_authentication.py +21 -9
- py2docfx/venv/venv1/Lib/site-packages/azure/core/pipeline/policies/_authentication_async.py +21 -9
- py2docfx/venv/venv1/Lib/site-packages/azure/core/pipeline/policies/_retry.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_bearer_token_provider.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/authorization_code.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/azd_cli.py +82 -17
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/azure_cli.py +28 -5
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/azure_powershell.py +28 -4
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/broker.py +79 -0
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/chained.py +9 -3
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/default.py +153 -53
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/imds.py +25 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/shared_cache.py +12 -5
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/vscode.py +163 -144
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/workload_identity.py +23 -12
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/__init__.py +4 -0
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/interactive.py +14 -2
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/pipeline.py +4 -2
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/utils.py +96 -0
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_version.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_bearer_token_provider.py +3 -3
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/authorization_code.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/azd_cli.py +32 -13
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/azure_cli.py +26 -5
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/azure_powershell.py +13 -2
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/chained.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/default.py +120 -55
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/imds.py +27 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/on_behalf_of.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/shared_cache.py +12 -5
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/vscode.py +15 -67
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/workload_identity.py +17 -13
- py2docfx/venv/venv1/Lib/site-packages/cffi/__init__.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/cffi/cparser.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/cffi/recompiler.py +5 -5
- py2docfx/venv/venv1/Lib/site-packages/cffi/setuptools_ext.py +13 -0
- py2docfx/venv/venv1/Lib/site-packages/cffi/vengine_cpy.py +3 -0
- py2docfx/venv/venv1/Lib/site-packages/charset_normalizer/api.py +3 -2
- py2docfx/venv/venv1/Lib/site-packages/charset_normalizer/legacy.py +17 -1
- py2docfx/venv/venv1/Lib/site-packages/charset_normalizer/version.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/cryptography/__about__.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/cryptography/__init__.py +0 -13
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/_oid.py +8 -0
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/asn1/__init__.py +10 -0
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/asn1/asn1.py +116 -0
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py +3 -9
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi +32 -0
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi +23 -0
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi +1 -13
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py +16 -0
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py +16 -1
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py +0 -2
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py +8 -0
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py +0 -47
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py +6 -91
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py +1 -3
- py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/cryptography/utils.py +0 -2
- py2docfx/venv/venv1/Lib/site-packages/cryptography/x509/name.py +2 -3
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/__init__.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/any_pb2.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/api_pb2.py +12 -8
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/compiler/plugin_pb2.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/descriptor.py +398 -246
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/descriptor_pb2.py +74 -72
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/descriptor_pool.py +5 -4
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/duration_pb2.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/empty_pb2.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/field_mask_pb2.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/api_implementation.py +0 -6
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/extension_dict.py +3 -3
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/field_mask.py +3 -3
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/python_edition_defaults.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/python_message.py +10 -2
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/type_checkers.py +47 -5
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/json_format.py +55 -32
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/runtime_version.py +6 -26
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/source_context_pb2.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/struct_pb2.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/text_format.py +30 -19
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/timestamp_pb2.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/type_pb2.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/google/protobuf/wrappers_pb2.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/pycparser/__init__.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/pycparser/c_generator.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/pycparser/c_lexer.py +14 -0
- py2docfx/venv/venv1/Lib/site-packages/pycparser/c_parser.py +30 -7
- py2docfx/venv/venv1/Lib/site-packages/pycparser/lextab.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/pycparser/yacctab.py +132 -127
- py2docfx/venv/venv1/Lib/site-packages/requests/__version__.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/requests/adapters.py +17 -40
- py2docfx/venv/venv1/Lib/site-packages/requests/sessions.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/typing_extensions.py +91 -18
- {py2docfx-0.1.20rc2195074.dist-info → py2docfx-0.1.20rc2245107.dist-info}/METADATA +1 -1
- {py2docfx-0.1.20rc2195074.dist-info → py2docfx-0.1.20rc2245107.dist-info}/RECORD +104 -103
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/linux_vscode_adapter.py +0 -100
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/macos_vscode_adapter.py +0 -34
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/win_vscode_adapter.py +0 -77
- {py2docfx-0.1.20rc2195074.dist-info → py2docfx-0.1.20rc2245107.dist-info}/WHEEL +0 -0
- {py2docfx-0.1.20rc2195074.dist-info → py2docfx-0.1.20rc2245107.dist-info}/top_level.txt +0 -0
@@ -21,10 +21,12 @@ from ..._credentials.azd_cli import (
|
|
21
21
|
EXECUTABLE_NAME,
|
22
22
|
get_safe_working_dir,
|
23
23
|
NOT_LOGGED_IN,
|
24
|
+
UNKNOWN_CLAIMS_FLAG,
|
24
25
|
parse_token,
|
25
26
|
sanitize_output,
|
27
|
+
extract_cli_error_message,
|
26
28
|
)
|
27
|
-
from ..._internal import resolve_tenant, within_dac, validate_tenant_id, validate_scope
|
29
|
+
from ..._internal import encode_base64, resolve_tenant, within_dac, validate_tenant_id, validate_scope
|
28
30
|
|
29
31
|
|
30
32
|
_LOGGER = logging.getLogger(__name__)
|
@@ -87,7 +89,7 @@ class AzureDeveloperCliCredential(AsyncContextManager):
|
|
87
89
|
async def get_token(
|
88
90
|
self,
|
89
91
|
*scopes: str,
|
90
|
-
claims: Optional[str] = None,
|
92
|
+
claims: Optional[str] = None,
|
91
93
|
tenant_id: Optional[str] = None,
|
92
94
|
**kwargs: Any,
|
93
95
|
) -> AccessToken:
|
@@ -99,7 +101,8 @@ class AzureDeveloperCliCredential(AsyncContextManager):
|
|
99
101
|
:param str scopes: desired scope for the access token. This credential allows only one scope per request.
|
100
102
|
For more information about scopes, see
|
101
103
|
https://learn.microsoft.com/entra/identity-platform/scopes-oidc.
|
102
|
-
:keyword str claims:
|
104
|
+
:keyword str claims: additional claims required in the token, such as those returned in a resource provider's
|
105
|
+
claims challenge following an authorization failure.
|
103
106
|
:keyword str tenant_id: optional tenant to include in the token request.
|
104
107
|
|
105
108
|
:return: An access token with the desired scopes.
|
@@ -110,11 +113,13 @@ class AzureDeveloperCliCredential(AsyncContextManager):
|
|
110
113
|
"""
|
111
114
|
# only ProactorEventLoop supports subprocesses on Windows (and it isn't the default loop on Python < 3.8)
|
112
115
|
if sys.platform.startswith("win") and not isinstance(asyncio.get_event_loop(), asyncio.ProactorEventLoop):
|
113
|
-
return _SyncAzureDeveloperCliCredential().get_token(*scopes, tenant_id=tenant_id, **kwargs)
|
116
|
+
return _SyncAzureDeveloperCliCredential().get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
|
114
117
|
|
115
118
|
options: TokenRequestOptions = {}
|
116
119
|
if tenant_id:
|
117
120
|
options["tenant_id"] = tenant_id
|
121
|
+
if claims:
|
122
|
+
options["claims"] = claims
|
118
123
|
|
119
124
|
token_info = await self._get_token_base(*scopes, options=options, **kwargs)
|
120
125
|
return AccessToken(token_info.token, token_info.expires_on)
|
@@ -152,6 +157,7 @@ class AzureDeveloperCliCredential(AsyncContextManager):
|
|
152
157
|
raise ValueError("Missing scope in request. \n")
|
153
158
|
|
154
159
|
tenant_id = options.get("tenant_id") if options else None
|
160
|
+
claims = options.get("claims") if options else None
|
155
161
|
if tenant_id:
|
156
162
|
validate_tenant_id(tenant_id)
|
157
163
|
for scope in scopes:
|
@@ -169,16 +175,22 @@ class AzureDeveloperCliCredential(AsyncContextManager):
|
|
169
175
|
|
170
176
|
if tenant:
|
171
177
|
command_args += ["--tenant-id", tenant]
|
178
|
+
if claims:
|
179
|
+
command_args += ["--claims", encode_base64(claims)]
|
172
180
|
output = await _run_command(command_args, self._process_timeout)
|
173
181
|
|
174
182
|
token = parse_token(output)
|
175
183
|
if not token:
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
184
|
+
extracted = extract_cli_error_message(output)
|
185
|
+
if extracted:
|
186
|
+
message = extracted
|
187
|
+
else:
|
188
|
+
sanitized_output = sanitize_output(output)
|
189
|
+
message = (
|
190
|
+
f"Unexpected output from Azure Developer CLI: '{sanitized_output}'. \n"
|
191
|
+
f"To mitigate this issue, please refer to the troubleshooting guidelines here at "
|
192
|
+
f"https://aka.ms/azsdk/python/identity/azdevclicredential/troubleshoot."
|
193
|
+
)
|
182
194
|
if within_dac.get():
|
183
195
|
raise CredentialUnavailableError(message=message)
|
184
196
|
raise ClientAuthenticationError(message=message)
|
@@ -226,10 +238,17 @@ async def _run_command(command_args: List[str], timeout: int) -> str:
|
|
226
238
|
if proc.returncode == 127 or stderr.startswith("'azd' is not recognized"):
|
227
239
|
raise CredentialUnavailableError(CLI_NOT_FOUND)
|
228
240
|
|
229
|
-
|
241
|
+
combined_text = f"{output}\n{stderr}"
|
242
|
+
if "not logged in, run `azd auth login` to login" in combined_text and "AADSTS" not in combined_text:
|
230
243
|
raise CredentialUnavailableError(message=NOT_LOGGED_IN)
|
231
|
-
|
232
|
-
|
244
|
+
if "unknown flag: --claims" in combined_text:
|
245
|
+
raise CredentialUnavailableError(message=UNKNOWN_CLAIMS_FLAG)
|
246
|
+
|
247
|
+
message = (
|
248
|
+
extract_cli_error_message(output)
|
249
|
+
or extract_cli_error_message(stderr)
|
250
|
+
or (sanitize_output(stderr) if stderr else "Failed to invoke Azure Developer CLI")
|
251
|
+
)
|
233
252
|
if within_dac.get():
|
234
253
|
raise CredentialUnavailableError(message=message)
|
235
254
|
raise ClientAuthenticationError(message=message)
|
@@ -23,9 +23,11 @@ from ..._credentials.azure_cli import (
|
|
23
23
|
NOT_LOGGED_IN,
|
24
24
|
parse_token,
|
25
25
|
sanitize_output,
|
26
|
+
CLAIMS_UNSUPPORTED_ERROR,
|
26
27
|
)
|
27
28
|
from ..._internal import (
|
28
29
|
_scopes_to_resource,
|
30
|
+
encode_base64,
|
29
31
|
resolve_tenant,
|
30
32
|
within_dac,
|
31
33
|
validate_tenant_id,
|
@@ -82,7 +84,7 @@ class AzureCliCredential(AsyncContextManager):
|
|
82
84
|
async def get_token(
|
83
85
|
self,
|
84
86
|
*scopes: str,
|
85
|
-
claims: Optional[str] = None,
|
87
|
+
claims: Optional[str] = None,
|
86
88
|
tenant_id: Optional[str] = None,
|
87
89
|
**kwargs: Any,
|
88
90
|
) -> AccessToken:
|
@@ -94,22 +96,26 @@ class AzureCliCredential(AsyncContextManager):
|
|
94
96
|
:param str scopes: desired scope for the access token. This credential allows only one scope per request.
|
95
97
|
For more information about scopes, see
|
96
98
|
https://learn.microsoft.com/entra/identity-platform/scopes-oidc.
|
97
|
-
:keyword str claims:
|
99
|
+
:keyword str claims: additional claims required in the token. This credential does not support claims
|
100
|
+
challenges.
|
98
101
|
:keyword str tenant_id: optional tenant to include in the token request.
|
99
102
|
|
100
103
|
:return: An access token with the desired scopes.
|
101
104
|
:rtype: ~azure.core.credentials.AccessToken
|
102
|
-
:raises ~azure.identity.CredentialUnavailableError: the credential was unable to invoke the Azure CLI
|
105
|
+
:raises ~azure.identity.CredentialUnavailableError: the credential was either unable to invoke the Azure CLI
|
106
|
+
or a claims challenge was provided.
|
103
107
|
:raises ~azure.core.exceptions.ClientAuthenticationError: the credential invoked the Azure CLI but didn't
|
104
108
|
receive an access token.
|
105
109
|
"""
|
106
110
|
# only ProactorEventLoop supports subprocesses on Windows (and it isn't the default loop on Python < 3.8)
|
107
111
|
if sys.platform.startswith("win") and not isinstance(asyncio.get_event_loop(), asyncio.ProactorEventLoop):
|
108
|
-
return _SyncAzureCliCredential().get_token(*scopes, tenant_id=tenant_id, **kwargs)
|
112
|
+
return _SyncAzureCliCredential().get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
|
109
113
|
|
110
114
|
options: TokenRequestOptions = {}
|
111
115
|
if tenant_id:
|
112
116
|
options["tenant_id"] = tenant_id
|
117
|
+
if claims:
|
118
|
+
options["claims"] = claims
|
113
119
|
|
114
120
|
token_info = await self._get_token_base(*scopes, options=options, **kwargs)
|
115
121
|
return AccessToken(token_info.token, token_info.expires_on)
|
@@ -130,7 +136,8 @@ class AzureCliCredential(AsyncContextManager):
|
|
130
136
|
:rtype: ~azure.core.credentials.AccessTokenInfo
|
131
137
|
:return: An AccessTokenInfo instance containing information about the token.
|
132
138
|
|
133
|
-
:raises ~azure.identity.CredentialUnavailableError: the credential was unable to invoke the Azure CLI
|
139
|
+
:raises ~azure.identity.CredentialUnavailableError: the credential was either unable to invoke the Azure CLI
|
140
|
+
or a claims challenge was provided.
|
134
141
|
:raises ~azure.core.exceptions.ClientAuthenticationError: the credential invoked the Azure CLI but didn't
|
135
142
|
receive an access token.
|
136
143
|
"""
|
@@ -142,6 +149,20 @@ class AzureCliCredential(AsyncContextManager):
|
|
142
149
|
async def _get_token_base(
|
143
150
|
self, *scopes: str, options: Optional[TokenRequestOptions] = None, **kwargs: Any
|
144
151
|
) -> AccessTokenInfo:
|
152
|
+
# Check for claims challenge first
|
153
|
+
if options and "claims" in options and options["claims"]:
|
154
|
+
error_message = CLAIMS_UNSUPPORTED_ERROR.format(claims_value=encode_base64(options["claims"]))
|
155
|
+
|
156
|
+
# Add tenant if provided in options
|
157
|
+
if options.get("tenant_id"):
|
158
|
+
error_message += f" --tenant {options.get('tenant_id')}"
|
159
|
+
|
160
|
+
# Add scope if provided
|
161
|
+
if scopes:
|
162
|
+
error_message += f" --scope {scopes[0]}"
|
163
|
+
|
164
|
+
raise CredentialUnavailableError(message=error_message)
|
165
|
+
|
145
166
|
tenant_id = options.get("tenant_id") if options else None
|
146
167
|
if tenant_id:
|
147
168
|
validate_tenant_id(tenant_id)
|
@@ -16,8 +16,9 @@ from ..._credentials.azure_powershell import (
|
|
16
16
|
get_safe_working_dir,
|
17
17
|
raise_for_error,
|
18
18
|
parse_token,
|
19
|
+
CLAIMS_UNSUPPORTED_ERROR,
|
19
20
|
)
|
20
|
-
from ..._internal import resolve_tenant, validate_tenant_id, validate_scope
|
21
|
+
from ..._internal import encode_base64, resolve_tenant, validate_tenant_id, validate_scope
|
21
22
|
|
22
23
|
|
23
24
|
class AzurePowerShellCredential(AsyncContextManager):
|
@@ -58,7 +59,7 @@ class AzurePowerShellCredential(AsyncContextManager):
|
|
58
59
|
async def get_token(
|
59
60
|
self,
|
60
61
|
*scopes: str,
|
61
|
-
claims: Optional[str] = None,
|
62
|
+
claims: Optional[str] = None,
|
62
63
|
tenant_id: Optional[str] = None,
|
63
64
|
**kwargs: Any,
|
64
65
|
) -> AccessToken:
|
@@ -87,6 +88,8 @@ class AzurePowerShellCredential(AsyncContextManager):
|
|
87
88
|
options: TokenRequestOptions = {}
|
88
89
|
if tenant_id:
|
89
90
|
options["tenant_id"] = tenant_id
|
91
|
+
if claims:
|
92
|
+
options["claims"] = claims
|
90
93
|
|
91
94
|
token_info = await self._get_token_base(*scopes, options=options, **kwargs)
|
92
95
|
return AccessToken(token_info.token, token_info.expires_on)
|
@@ -119,6 +122,14 @@ class AzurePowerShellCredential(AsyncContextManager):
|
|
119
122
|
async def _get_token_base(
|
120
123
|
self, *scopes: str, options: Optional[TokenRequestOptions] = None, **kwargs: Any
|
121
124
|
) -> AccessTokenInfo:
|
125
|
+
|
126
|
+
# Check if claims challenge is provided
|
127
|
+
if options and "claims" in options and options["claims"]:
|
128
|
+
error_message = CLAIMS_UNSUPPORTED_ERROR.format(claims_value=encode_base64(options["claims"]))
|
129
|
+
if options.get("tenant_id"):
|
130
|
+
error_message += f" -Tenant {options.get('tenant_id')}"
|
131
|
+
raise CredentialUnavailableError(message=error_message)
|
132
|
+
|
122
133
|
tenant_id = options.get("tenant_id") if options else None
|
123
134
|
if tenant_id:
|
124
135
|
validate_tenant_id(tenant_id)
|
@@ -25,7 +25,7 @@ class ChainedTokenCredential(AsyncContextManager):
|
|
25
25
|
https://aka.ms/azsdk/python/identity/credential-chains#chainedtokencredential-overview.
|
26
26
|
|
27
27
|
:param credentials: credential instances to form the chain
|
28
|
-
:type credentials: ~azure.core.credentials_async.
|
28
|
+
:type credentials: ~azure.core.credentials_async.AsyncTokenProvider
|
29
29
|
|
30
30
|
.. admonition:: Example:
|
31
31
|
|
@@ -8,8 +8,9 @@ from typing import List, Optional, Any, cast
|
|
8
8
|
|
9
9
|
from azure.core.credentials import AccessToken, AccessTokenInfo, TokenRequestOptions
|
10
10
|
from azure.core.credentials_async import AsyncTokenCredential, AsyncSupportsTokenInfo
|
11
|
+
from ... import CredentialUnavailableError
|
11
12
|
from ..._constants import EnvironmentVariables
|
12
|
-
from ..._internal import get_default_authority, normalize_authority, within_dac
|
13
|
+
from ..._internal import get_default_authority, normalize_authority, within_dac, process_credential_exclusions
|
13
14
|
from .azure_cli import AzureCliCredential
|
14
15
|
from .azd_cli import AzureDeveloperCliCredential
|
15
16
|
from .azure_powershell import AzurePowerShellCredential
|
@@ -24,6 +25,34 @@ from .workload_identity import WorkloadIdentityCredential
|
|
24
25
|
_LOGGER = logging.getLogger(__name__)
|
25
26
|
|
26
27
|
|
28
|
+
class AsyncFailedDACCredential:
|
29
|
+
"""Async version of FailedDACCredential for use in async credential chains.
|
30
|
+
|
31
|
+
This acts as a substitute for an async credential that has failed to initialize in the DAC chain.
|
32
|
+
"""
|
33
|
+
|
34
|
+
def __init__(self, credential_name: str, error: str) -> None:
|
35
|
+
self._error = error
|
36
|
+
self._credential_name = credential_name
|
37
|
+
|
38
|
+
async def get_token(self, *scopes: str, **kwargs: Any) -> AccessToken:
|
39
|
+
raise CredentialUnavailableError(self._error)
|
40
|
+
|
41
|
+
async def get_token_info(
|
42
|
+
self, *scopes, options: Optional[TokenRequestOptions] = None, **kwargs: Any
|
43
|
+
) -> AccessTokenInfo:
|
44
|
+
raise CredentialUnavailableError(self._error)
|
45
|
+
|
46
|
+
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
|
47
|
+
pass
|
48
|
+
|
49
|
+
async def __aenter__(self) -> "AsyncFailedDACCredential":
|
50
|
+
return self
|
51
|
+
|
52
|
+
async def close(self) -> None:
|
53
|
+
pass
|
54
|
+
|
55
|
+
|
27
56
|
class DefaultAzureCredential(ChainedTokenCredential):
|
28
57
|
"""A credential capable of handling most Azure SDK authentication scenarios. See
|
29
58
|
https://aka.ms/azsdk/python/identity/credential-chains#usage-guidance-for-defaultazurecredential.
|
@@ -73,11 +102,13 @@ class DefaultAzureCredential(ChainedTokenCredential):
|
|
73
102
|
:keyword str shared_cache_tenant_id: Preferred tenant for :class:`~azure.identity.aio.SharedTokenCacheCredential`.
|
74
103
|
Defaults to the value of environment variable AZURE_TENANT_ID, if any.
|
75
104
|
:keyword str visual_studio_code_tenant_id: Tenant ID to use when authenticating with
|
76
|
-
:class:`~azure.identity.
|
77
|
-
|
78
|
-
Directory work or school accounts.
|
105
|
+
:class:`~azure.identity.VisualStudioCodeCredential`. Defaults to the tenant specified in the authentication
|
106
|
+
record file used by the Azure Resources extension.
|
79
107
|
:keyword int process_timeout: The timeout in seconds to use for developer credentials that run
|
80
108
|
subprocesses (e.g. AzureCliCredential, AzurePowerShellCredential). Defaults to **10** seconds.
|
109
|
+
:keyword bool require_envvar: If **True**, require that the AZURE_TOKEN_CREDENTIALS environment variable be set
|
110
|
+
to a value denoting the credential type or credential group to use. If unset or empty, DefaultAzureCredential
|
111
|
+
will raise a `ValueError`. Defaults to **False**.
|
81
112
|
|
82
113
|
.. admonition:: Example:
|
83
114
|
|
@@ -89,23 +120,15 @@ class DefaultAzureCredential(ChainedTokenCredential):
|
|
89
120
|
:caption: Create a DefaultAzureCredential.
|
90
121
|
"""
|
91
122
|
|
92
|
-
def __init__(self, **kwargs: Any) -> None: # pylint: disable=too-many-statements
|
123
|
+
def __init__(self, **kwargs: Any) -> None: # pylint: disable=too-many-statements, too-many-locals
|
93
124
|
if "tenant_id" in kwargs:
|
94
125
|
raise TypeError("'tenant_id' is not supported in DefaultAzureCredential.")
|
95
126
|
|
96
127
|
authority = kwargs.pop("authority", None)
|
97
|
-
|
98
|
-
vscode_tenant_id = kwargs.pop(
|
99
|
-
"visual_studio_code_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
|
100
|
-
)
|
101
|
-
vscode_args = dict(kwargs)
|
102
|
-
if authority:
|
103
|
-
vscode_args["authority"] = authority
|
104
|
-
if vscode_tenant_id:
|
105
|
-
vscode_args["tenant_id"] = vscode_tenant_id
|
106
|
-
|
107
128
|
authority = normalize_authority(authority) if authority else get_default_authority()
|
108
129
|
|
130
|
+
vscode_tenant_id = kwargs.pop("visual_studio_code_tenant_id", None)
|
131
|
+
|
109
132
|
shared_cache_username = kwargs.pop("shared_cache_username", os.environ.get(EnvironmentVariables.AZURE_USERNAME))
|
110
133
|
shared_cache_tenant_id = kwargs.pop(
|
111
134
|
"shared_cache_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
|
@@ -119,56 +142,94 @@ class DefaultAzureCredential(ChainedTokenCredential):
|
|
119
142
|
"workload_identity_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
|
120
143
|
)
|
121
144
|
|
122
|
-
vscode_tenant_id = kwargs.pop(
|
123
|
-
"visual_studio_code_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
|
124
|
-
)
|
125
|
-
|
126
145
|
process_timeout = kwargs.pop("process_timeout", 10)
|
127
|
-
|
128
|
-
|
129
|
-
exclude_workload_identity_credential = kwargs.pop("exclude_workload_identity_credential", False)
|
130
|
-
exclude_visual_studio_code_credential = kwargs.pop("exclude_visual_studio_code_credential", True)
|
131
|
-
exclude_developer_cli_credential = kwargs.pop("exclude_developer_cli_credential", False)
|
132
|
-
exclude_cli_credential = kwargs.pop("exclude_cli_credential", False)
|
133
|
-
exclude_environment_credential = kwargs.pop("exclude_environment_credential", False)
|
134
|
-
exclude_managed_identity_credential = kwargs.pop("exclude_managed_identity_credential", False)
|
135
|
-
exclude_shared_token_cache_credential = kwargs.pop("exclude_shared_token_cache_credential", False)
|
136
|
-
exclude_powershell_credential = kwargs.pop("exclude_powershell_credential", False)
|
137
|
-
|
138
|
-
if token_credentials_env == "dev":
|
139
|
-
# In dev mode, use only developer credentials
|
140
|
-
exclude_environment_credential = True
|
141
|
-
exclude_managed_identity_credential = True
|
142
|
-
exclude_workload_identity_credential = True
|
143
|
-
elif token_credentials_env == "prod":
|
144
|
-
# In prod mode, use only production credentials
|
145
|
-
exclude_shared_token_cache_credential = True
|
146
|
-
exclude_visual_studio_code_credential = True
|
147
|
-
exclude_cli_credential = True
|
148
|
-
exclude_developer_cli_credential = True
|
149
|
-
exclude_powershell_credential = True
|
150
|
-
elif token_credentials_env != "":
|
151
|
-
# If the environment variable is set to something other than dev or prod, raise an error
|
146
|
+
require_envvar = kwargs.pop("require_envvar", False)
|
147
|
+
if require_envvar and not os.environ.get(EnvironmentVariables.AZURE_TOKEN_CREDENTIALS):
|
152
148
|
raise ValueError(
|
153
|
-
|
154
|
-
"
|
149
|
+
"AZURE_TOKEN_CREDENTIALS environment variable is required but is not set or is empty. "
|
150
|
+
"Set it to 'dev', 'prod', or a specific credential name."
|
155
151
|
)
|
156
152
|
|
153
|
+
# Define credential configuration mapping (async version)
|
154
|
+
credential_config = {
|
155
|
+
"environment": {
|
156
|
+
"exclude_param": "exclude_environment_credential",
|
157
|
+
"env_name": "environmentcredential",
|
158
|
+
"default_exclude": False,
|
159
|
+
},
|
160
|
+
"workload_identity": {
|
161
|
+
"exclude_param": "exclude_workload_identity_credential",
|
162
|
+
"env_name": "workloadidentitycredential",
|
163
|
+
"default_exclude": False,
|
164
|
+
},
|
165
|
+
"managed_identity": {
|
166
|
+
"exclude_param": "exclude_managed_identity_credential",
|
167
|
+
"env_name": "managedidentitycredential",
|
168
|
+
"default_exclude": False,
|
169
|
+
},
|
170
|
+
"shared_token_cache": {
|
171
|
+
"exclude_param": "exclude_shared_token_cache_credential",
|
172
|
+
"default_exclude": False,
|
173
|
+
},
|
174
|
+
"visual_studio_code": {
|
175
|
+
"exclude_param": "exclude_visual_studio_code_credential",
|
176
|
+
"env_name": "visualstudiocodecredential",
|
177
|
+
"default_exclude": False,
|
178
|
+
},
|
179
|
+
"cli": {
|
180
|
+
"exclude_param": "exclude_cli_credential",
|
181
|
+
"env_name": "azureclicredential",
|
182
|
+
"default_exclude": False,
|
183
|
+
},
|
184
|
+
"developer_cli": {
|
185
|
+
"exclude_param": "exclude_developer_cli_credential",
|
186
|
+
"env_name": "azuredeveloperclicredential",
|
187
|
+
"default_exclude": False,
|
188
|
+
},
|
189
|
+
"powershell": {
|
190
|
+
"exclude_param": "exclude_powershell_credential",
|
191
|
+
"env_name": "azurepowershellcredential",
|
192
|
+
"default_exclude": False,
|
193
|
+
},
|
194
|
+
}
|
195
|
+
|
196
|
+
# Extract user-provided exclude flags and set defaults
|
197
|
+
exclude_flags = {}
|
198
|
+
user_excludes = {}
|
199
|
+
for cred_key, config in credential_config.items():
|
200
|
+
param_name = cast(str, config["exclude_param"])
|
201
|
+
user_excludes[cred_key] = kwargs.pop(param_name, None)
|
202
|
+
exclude_flags[cred_key] = config["default_exclude"]
|
203
|
+
|
204
|
+
# Process AZURE_TOKEN_CREDENTIALS environment variable and apply user overrides
|
205
|
+
exclude_flags = process_credential_exclusions(credential_config, exclude_flags, user_excludes)
|
206
|
+
|
207
|
+
# Extract individual exclude flags for backward compatibility
|
208
|
+
exclude_environment_credential = exclude_flags["environment"]
|
209
|
+
exclude_workload_identity_credential = exclude_flags["workload_identity"]
|
210
|
+
exclude_managed_identity_credential = exclude_flags["managed_identity"]
|
211
|
+
exclude_shared_token_cache_credential = exclude_flags["shared_token_cache"]
|
212
|
+
exclude_visual_studio_code_credential = exclude_flags["visual_studio_code"]
|
213
|
+
exclude_cli_credential = exclude_flags["cli"]
|
214
|
+
exclude_developer_cli_credential = exclude_flags["developer_cli"]
|
215
|
+
exclude_powershell_credential = exclude_flags["powershell"]
|
216
|
+
|
157
217
|
credentials: List[AsyncSupportsTokenInfo] = []
|
158
218
|
within_dac.set(True)
|
159
219
|
if not exclude_environment_credential:
|
160
220
|
credentials.append(EnvironmentCredential(authority=authority, _within_dac=True, **kwargs))
|
161
221
|
if not exclude_workload_identity_credential:
|
162
|
-
|
163
|
-
client_id = workload_identity_client_id
|
222
|
+
try:
|
164
223
|
credentials.append(
|
165
224
|
WorkloadIdentityCredential(
|
166
|
-
client_id=cast(str,
|
225
|
+
client_id=cast(str, workload_identity_client_id),
|
167
226
|
tenant_id=workload_identity_tenant_id,
|
168
|
-
token_file_path=os.environ
|
227
|
+
token_file_path=os.environ.get(EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE),
|
169
228
|
**kwargs,
|
170
229
|
)
|
171
230
|
)
|
231
|
+
except ValueError as ex:
|
232
|
+
credentials.append(AsyncFailedDACCredential("WorkloadIdentityCredential", error=str(ex)))
|
172
233
|
if not exclude_managed_identity_credential:
|
173
234
|
credentials.append(
|
174
235
|
ManagedIdentityCredential(
|
@@ -187,7 +248,7 @@ class DefaultAzureCredential(ChainedTokenCredential):
|
|
187
248
|
except Exception as ex: # pylint:disable=broad-except
|
188
249
|
_LOGGER.info("Shared token cache is unavailable: '%s'", ex)
|
189
250
|
if not exclude_visual_studio_code_credential:
|
190
|
-
credentials.append(VisualStudioCodeCredential(
|
251
|
+
credentials.append(VisualStudioCodeCredential(tenant_id=vscode_tenant_id))
|
191
252
|
if not exclude_cli_credential:
|
192
253
|
credentials.append(AzureCliCredential(process_timeout=process_timeout))
|
193
254
|
if not exclude_powershell_credential:
|
@@ -226,8 +287,10 @@ class DefaultAzureCredential(ChainedTokenCredential):
|
|
226
287
|
return token
|
227
288
|
|
228
289
|
within_dac.set(True)
|
229
|
-
|
230
|
-
|
290
|
+
try:
|
291
|
+
token = await super().get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
|
292
|
+
finally:
|
293
|
+
within_dac.set(False)
|
231
294
|
return token
|
232
295
|
|
233
296
|
async def get_token_info(self, *scopes: str, options: Optional[TokenRequestOptions] = None) -> AccessTokenInfo:
|
@@ -257,6 +320,8 @@ class DefaultAzureCredential(ChainedTokenCredential):
|
|
257
320
|
return token_info
|
258
321
|
|
259
322
|
within_dac.set(True)
|
260
|
-
|
261
|
-
|
323
|
+
try:
|
324
|
+
token_info = await cast(AsyncSupportsTokenInfo, super()).get_token_info(*scopes, options=options)
|
325
|
+
finally:
|
326
|
+
within_dac.set(False)
|
262
327
|
return token_info
|
@@ -3,10 +3,13 @@
|
|
3
3
|
# Licensed under the MIT License.
|
4
4
|
# ------------------------------------
|
5
5
|
import os
|
6
|
-
from typing import Optional, Any
|
6
|
+
from typing import Optional, Any, Dict
|
7
7
|
|
8
8
|
from azure.core.exceptions import ClientAuthenticationError, HttpResponseError
|
9
9
|
from azure.core.credentials import AccessTokenInfo
|
10
|
+
from azure.core.pipeline.policies import AsyncRetryPolicy
|
11
|
+
from azure.core.pipeline import PipelineResponse
|
12
|
+
|
10
13
|
from ... import CredentialUnavailableError
|
11
14
|
from ..._constants import EnvironmentVariables
|
12
15
|
from .._internal import AsyncContextManager
|
@@ -16,10 +19,33 @@ from ..._internal import within_credential_chain
|
|
16
19
|
from ..._credentials.imds import _get_request, _check_forbidden_response, PIPELINE_SETTINGS
|
17
20
|
|
18
21
|
|
22
|
+
class AsyncImdsRetryPolicy(AsyncRetryPolicy):
|
23
|
+
"""Async custom retry policy for IMDS credential with extended retry duration for 410 responses.
|
24
|
+
|
25
|
+
This policy ensures that specifically for 410 status codes, the total exponential backoff duration
|
26
|
+
is at least 70 seconds to handle temporary IMDS endpoint unavailability.
|
27
|
+
For other status codes, it uses the standard retry behavior.
|
28
|
+
"""
|
29
|
+
|
30
|
+
def __init__(self, **kwargs: Any) -> None:
|
31
|
+
# Increased backoff factor to ensure at least 70 seconds retry duration for 410 responses.
|
32
|
+
# Five retries, with each retry sleeping for [0.0s, 5.0s, 10.0s, 20.0s, 40.0s] between attempts (75s total)
|
33
|
+
self.backoff_factor_for_410 = 2.5
|
34
|
+
super().__init__(**kwargs)
|
35
|
+
|
36
|
+
def is_retry(self, settings: Dict[str, Any], response: PipelineResponse[Any, Any]) -> bool:
|
37
|
+
if response.http_response.status_code == 410:
|
38
|
+
settings["backoff"] = self.backoff_factor_for_410
|
39
|
+
else:
|
40
|
+
settings["backoff"] = self.backoff_factor
|
41
|
+
return super().is_retry(settings, response)
|
42
|
+
|
43
|
+
|
19
44
|
class ImdsCredential(AsyncContextManager, GetTokenMixin):
|
20
45
|
def __init__(self, **kwargs: Any) -> None:
|
21
46
|
super().__init__()
|
22
47
|
|
48
|
+
kwargs["retry_policy_class"] = AsyncImdsRetryPolicy
|
23
49
|
self._client = AsyncManagedIdentityClient(_get_request, **dict(PIPELINE_SETTINGS, **kwargs))
|
24
50
|
if EnvironmentVariables.AZURE_POD_IDENTITY_AUTHORITY_HOST in os.environ:
|
25
51
|
self._endpoint_available: Optional[bool] = True
|
@@ -119,7 +119,7 @@ class OnBehalfOfCredential(AsyncContextManager, GetTokenMixin):
|
|
119
119
|
# Note we assume the cache has tokens for one user only. That's okay because each instance of this class is
|
120
120
|
# locked to a single user (assertion). This assumption will become unsafe if this class allows applications
|
121
121
|
# to change an instance's assertion.
|
122
|
-
refresh_tokens = self._client.get_cached_refresh_tokens(scopes)
|
122
|
+
refresh_tokens = self._client.get_cached_refresh_tokens(scopes, **kwargs)
|
123
123
|
if len(refresh_tokens) == 1: # there should be only one
|
124
124
|
try:
|
125
125
|
refresh_token = refresh_tokens[0]["secret"]
|
@@ -8,6 +8,7 @@ from ..._internal.aad_client import AadClientBase
|
|
8
8
|
from ... import CredentialUnavailableError
|
9
9
|
from ..._constants import DEVELOPER_SIGN_ON_CLIENT_ID
|
10
10
|
from ..._internal.shared_token_cache import NO_TOKEN, SharedTokenCacheBase
|
11
|
+
from ..._internal.utils import within_dac
|
11
12
|
from .._internal import AsyncContextManager
|
12
13
|
from .._internal.aad_client import AadClient
|
13
14
|
from .._internal.decorators import log_get_token_async
|
@@ -143,11 +144,17 @@ class SharedTokenCacheCredential(SharedTokenCacheBase, AsyncContextManager):
|
|
143
144
|
|
144
145
|
# try each refresh token, returning the first access token acquired
|
145
146
|
for refresh_token in self._get_refresh_tokens(account, is_cae=is_cae):
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
147
|
+
try:
|
148
|
+
token = await cast(AadClient, self._client).obtain_token_by_refresh_token(
|
149
|
+
scopes, refresh_token, claims=claims, tenant_id=tenant_id, enable_cae=is_cae, **kwargs
|
150
|
+
)
|
151
|
+
return token
|
152
|
+
except Exception as e: # pylint: disable=broad-except
|
153
|
+
if within_dac.get():
|
154
|
+
raise CredentialUnavailableError( # pylint: disable=raise-missing-from
|
155
|
+
message=getattr(e, "message", str(e)), response=getattr(e, "response", None)
|
156
|
+
)
|
157
|
+
raise
|
151
158
|
raise CredentialUnavailableError(message=NO_TOKEN.format(account.get("username")))
|
152
159
|
|
153
160
|
def _get_auth_client(self, **kwargs: Any) -> AadClientBase:
|