|
22 | 22 | import requests |
23 | 23 | from requests_mock import Mocker |
24 | 24 |
|
25 | | -from pyiceberg.catalog.rest.auth import AuthManagerAdapter, BasicAuthManager, GoogleAuthManager, NoopAuthManager |
| 25 | +from pyiceberg.catalog.rest.auth import AuthManagerAdapter, BasicAuthManager, EntraAuthManager, GoogleAuthManager, NoopAuthManager |
26 | 26 |
|
27 | 27 | TEST_URI = "https://iceberg-test-catalog/" |
28 | 28 | GOOGLE_CREDS_URI = "https://oauth2.googleapis.com/token" |
@@ -153,3 +153,80 @@ def test_google_auth_manager_import_error() -> None: |
153 | 153 | with patch.dict("sys.modules", {"google.auth": None, "google.auth.transport.requests": None}): |
154 | 154 | with pytest.raises(ImportError, match="Google Auth libraries not found. Please install 'google-auth'."): |
155 | 155 | GoogleAuthManager() |
| 156 | + |
| 157 | + |
| 158 | +@patch("azure.identity.DefaultAzureCredential") |
| 159 | +def test_entra_auth_manager_default_credential(mock_default_cred: MagicMock, rest_mock: Mocker) -> None: |
| 160 | + """Test EntraAuthManager with DefaultAzureCredential.""" |
| 161 | + mock_credential_instance = MagicMock() |
| 162 | + mock_token = MagicMock() |
| 163 | + mock_token.token = "entra_default_token" |
| 164 | + mock_token.expires_on = 9999999999 # Far future timestamp |
| 165 | + mock_credential_instance.get_token.return_value = mock_token |
| 166 | + mock_default_cred.return_value = mock_credential_instance |
| 167 | + |
| 168 | + auth_manager = EntraAuthManager() |
| 169 | + session = requests.Session() |
| 170 | + session.auth = AuthManagerAdapter(auth_manager) |
| 171 | + session.get(TEST_URI) |
| 172 | + |
| 173 | + mock_default_cred.assert_called_once_with() |
| 174 | + mock_credential_instance.get_token.assert_called_once_with("https://storage.azure.com/.default") |
| 175 | + history = rest_mock.request_history |
| 176 | + assert len(history) == 1 |
| 177 | + actual_headers = history[0].headers |
| 178 | + assert actual_headers["Authorization"] == "Bearer entra_default_token" |
| 179 | + |
| 180 | + |
| 181 | +@patch("azure.identity.DefaultAzureCredential") |
| 182 | +def test_entra_auth_manager_with_managed_identity_client_id(mock_default_cred: MagicMock, rest_mock: Mocker) -> None: |
| 183 | + """Test EntraAuthManager with managed_identity_client_id passed to DefaultAzureCredential.""" |
| 184 | + mock_credential_instance = MagicMock() |
| 185 | + mock_token = MagicMock() |
| 186 | + mock_token.token = "entra_mi_token" |
| 187 | + mock_token.expires_on = 9999999999 |
| 188 | + mock_credential_instance.get_token.return_value = mock_token |
| 189 | + mock_default_cred.return_value = mock_credential_instance |
| 190 | + |
| 191 | + auth_manager = EntraAuthManager(managed_identity_client_id="user-assigned-client-id") |
| 192 | + session = requests.Session() |
| 193 | + session.auth = AuthManagerAdapter(auth_manager) |
| 194 | + session.get(TEST_URI) |
| 195 | + |
| 196 | + mock_default_cred.assert_called_once_with(managed_identity_client_id="user-assigned-client-id") |
| 197 | + mock_credential_instance.get_token.assert_called_once_with("https://storage.azure.com/.default") |
| 198 | + history = rest_mock.request_history |
| 199 | + assert len(history) == 1 |
| 200 | + actual_headers = history[0].headers |
| 201 | + assert actual_headers["Authorization"] == "Bearer entra_mi_token" |
| 202 | + |
| 203 | + |
| 204 | +@patch("azure.identity.DefaultAzureCredential") |
| 205 | +def test_entra_auth_manager_custom_scopes(mock_default_cred: MagicMock, rest_mock: Mocker) -> None: |
| 206 | + """Test EntraAuthManager with custom scopes.""" |
| 207 | + mock_credential_instance = MagicMock() |
| 208 | + mock_token = MagicMock() |
| 209 | + mock_token.token = "entra_custom_scope_token" |
| 210 | + mock_token.expires_on = 9999999999 |
| 211 | + mock_credential_instance.get_token.return_value = mock_token |
| 212 | + mock_default_cred.return_value = mock_credential_instance |
| 213 | + |
| 214 | + custom_scopes = ["https://datalake.azure.net/.default", "https://storage.azure.com/.default"] |
| 215 | + auth_manager = EntraAuthManager(scopes=custom_scopes) |
| 216 | + session = requests.Session() |
| 217 | + session.auth = AuthManagerAdapter(auth_manager) |
| 218 | + session.get(TEST_URI) |
| 219 | + |
| 220 | + mock_default_cred.assert_called_once_with() |
| 221 | + mock_credential_instance.get_token.assert_called_once_with(*custom_scopes) |
| 222 | + history = rest_mock.request_history |
| 223 | + assert len(history) == 1 |
| 224 | + actual_headers = history[0].headers |
| 225 | + assert actual_headers["Authorization"] == "Bearer entra_custom_scope_token" |
| 226 | + |
| 227 | + |
| 228 | +def test_entra_auth_manager_import_error() -> None: |
| 229 | + """Test EntraAuthManager raises ImportError if azure-identity is not installed.""" |
| 230 | + with patch.dict("sys.modules", {"azure.identity": None}): |
| 231 | + with pytest.raises(ImportError, match="Azure Identity library not found"): |
| 232 | + EntraAuthManager() |
0 commit comments