diff --git a/sdk/confidentialledger/azure-confidentialledger/CHANGELOG.md b/sdk/confidentialledger/azure-confidentialledger/CHANGELOG.md index 129dd85532b6..22366f06a123 100644 --- a/sdk/confidentialledger/azure-confidentialledger/CHANGELOG.md +++ b/sdk/confidentialledger/azure-confidentialledger/CHANGELOG.md @@ -1,5 +1,11 @@ # Release History +## 2.0.0b2 (2026-01-29) + +### Bugs Fixed + +- Fixed authentication failure on HTTP redirects by preserving sensitive headers during service-managed redirects within the Confidential Ledger endpoint. + ## 2.0.0b1 (2025-10-20) ### Features Added diff --git a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_client.py b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_client.py index 5547a18f3e68..92370f080501 100644 --- a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_client.py +++ b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_client.py @@ -52,7 +52,13 @@ def __init__( # pylint: disable=missing-client-constructor-parameter-credential self._config.custom_hook_policy, self._config.logging_policy, policies.DistributedTracingPolicy(**kwargs), - policies.SensitiveHeaderCleanupPolicy(**kwargs) if self._config.redirect_policy else None, + # Redirect cleanup is disabled to preserve authentication and ledger-specific headers + # on service-managed redirects. Confidential Ledger redirects are expected to stay within + # the same trusted ledger endpoint, so forwarding these sensitive headers is required + # for correct authentication behavior. + policies.SensitiveHeaderCleanupPolicy( + disable_redirect_cleanup=True, **kwargs + ) if self._config.redirect_policy else None, self._config.http_logging_policy, ] self._client: PipelineClient = PipelineClient(base_url=_endpoint, policies=_policies, **kwargs) diff --git a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_version.py b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_version.py index 0e00a6283246..8eb37199ee54 100644 --- a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_version.py +++ b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_version.py @@ -6,4 +6,4 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -VERSION = "2.0.0b1" +VERSION = "2.0.0b2" diff --git a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_client.py b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_client.py index a3e303ca7c68..7f49efc68499 100644 --- a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_client.py +++ b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_client.py @@ -52,7 +52,9 @@ def __init__( # pylint: disable=missing-client-constructor-parameter-credential self._config.custom_hook_policy, self._config.logging_policy, policies.DistributedTracingPolicy(**kwargs), - policies.SensitiveHeaderCleanupPolicy(**kwargs) if self._config.redirect_policy else None, + policies.SensitiveHeaderCleanupPolicy( + disable_redirect_cleanup=True, **kwargs + ) if self._config.redirect_policy else None, self._config.http_logging_policy, ] self._client: AsyncPipelineClient = AsyncPipelineClient(base_url=_endpoint, policies=_policies, **kwargs) diff --git a/sdk/confidentialledger/azure-confidentialledger/tests/test_client_configuration.py b/sdk/confidentialledger/azure-confidentialledger/tests/test_client_configuration.py new file mode 100644 index 000000000000..47459e6733dd --- /dev/null +++ b/sdk/confidentialledger/azure-confidentialledger/tests/test_client_configuration.py @@ -0,0 +1,98 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Unit tests for ConfidentialLedgerClient configuration.""" + +import pytest +from unittest.mock import patch, MagicMock +from azure.core.pipeline import policies + +# Import the generated client directly to test its policy configuration +from azure.confidentialledger._client import ConfidentialLedgerClient as GeneratedClient + + +class TestClientConfiguration: + """Tests for client configuration settings.""" + + def test_sensitive_header_cleanup_policy_disable_redirect_cleanup_enabled(self): + """Test that SensitiveHeaderCleanupPolicy has disable_redirect_cleanup=True. + + This ensures that authentication and ledger-specific headers are preserved + on service-managed redirects, which is required for correct authentication + behavior within the trusted Confidential Ledger endpoint. + """ + # Mock the PipelineClient to capture the policies passed to it + with patch("azure.confidentialledger._client.PipelineClient") as mock_pipeline_client: + mock_pipeline_client.return_value = MagicMock() + + # Create the generated client directly - this will trigger policy creation + # The generated client only requires ledger_endpoint + client = GeneratedClient( + ledger_endpoint="https://test-ledger.confidentialledger.azure.com" + ) + + # Get the policies argument passed to PipelineClient + call_args = mock_pipeline_client.call_args + policies_arg = call_args.kwargs.get("policies") or call_args[1].get("policies") + + # Find the SensitiveHeaderCleanupPolicy in the policies list + sensitive_header_policy = None + for policy in policies_arg: + if isinstance(policy, policies.SensitiveHeaderCleanupPolicy): + sensitive_header_policy = policy + break + + # Assert the policy exists and has disable_redirect_cleanup=True + assert sensitive_header_policy is not None, ( + "SensitiveHeaderCleanupPolicy should be present in the client's policies" + ) + assert sensitive_header_policy._disable_redirect_cleanup is True, ( + "SensitiveHeaderCleanupPolicy should have disable_redirect_cleanup=True " + "to preserve authentication headers on Confidential Ledger redirects" + ) + + client.close() + + def test_sensitive_header_cleanup_policy_is_in_correct_position(self): + """Test that SensitiveHeaderCleanupPolicy is positioned after authentication_policy. + + The policy should be placed after the authentication policy so that it can + properly handle the redirect cleanup for authentication headers. + """ + with patch("azure.confidentialledger._client.PipelineClient") as mock_pipeline_client: + mock_pipeline_client.return_value = MagicMock() + + client = GeneratedClient( + ledger_endpoint="https://test-ledger.confidentialledger.azure.com" + ) + + # Get the policies argument passed to PipelineClient + call_args = mock_pipeline_client.call_args + policies_arg = call_args.kwargs.get("policies") or call_args[1].get("policies") + + # Filter out None values + non_none_policies = [p for p in policies_arg if p is not None] + + # Find positions of key policies + sensitive_header_idx = None + distributed_tracing_idx = None + + for idx, policy in enumerate(non_none_policies): + if isinstance(policy, policies.SensitiveHeaderCleanupPolicy): + sensitive_header_idx = idx + elif isinstance(policy, policies.DistributedTracingPolicy): + distributed_tracing_idx = idx + + # SensitiveHeaderCleanupPolicy should come after DistributedTracingPolicy + assert sensitive_header_idx is not None, ( + "SensitiveHeaderCleanupPolicy should be present in the policies" + ) + assert distributed_tracing_idx is not None, ( + "DistributedTracingPolicy should be present in the policies" + ) + assert sensitive_header_idx > distributed_tracing_idx, ( + "SensitiveHeaderCleanupPolicy should be positioned after DistributedTracingPolicy" + ) + + client.close()