diff --git a/pyiceberg/catalog/rest.py b/pyiceberg/catalog/rest.py index 20a04d9c5b..1cc6bd27c0 100644 --- a/pyiceberg/catalog/rest.py +++ b/pyiceberg/catalog/rest.py @@ -112,6 +112,7 @@ class IdentifierKind(Enum): VIEW = "view" +ACCESS_DELEGATION_DEFAULT = "vended-credentials" AUTHORIZATION_HEADER = "Authorization" BEARER_PREFIX = "Bearer" CATALOG_SCOPE = "catalog" @@ -555,7 +556,7 @@ def _config_headers(self, session: Session) -> None: session.headers["Content-type"] = "application/json" session.headers["X-Client-Version"] = ICEBERG_REST_SPEC_VERSION session.headers["User-Agent"] = f"PyIceberg/{__version__}" - session.headers["X-Iceberg-Access-Delegation"] = "vended-credentials" + session.headers.setdefault("X-Iceberg-Access-Delegation", ACCESS_DELEGATION_DEFAULT) def _extract_headers_from_properties(self) -> Dict[str, str]: return {key[len(HEADER_PREFIX) :]: value for key, value in self.properties.items() if key.startswith(HEADER_PREFIX)} diff --git a/tests/catalog/test_rest.py b/tests/catalog/test_rest.py index f05e15df38..9d75154ae0 100644 --- a/tests/catalog/test_rest.py +++ b/tests/catalog/test_rest.py @@ -60,6 +60,7 @@ "X-Client-Version": "0.14.1", "User-Agent": f"PyIceberg/{pyiceberg.__version__}", "Authorization": f"Bearer {TEST_TOKEN}", + "X-Iceberg-Access-Delegation": "vended-credentials", } OAUTH_TEST_HEADERS = { "Content-type": "application/x-www-form-urlencoded", @@ -708,6 +709,38 @@ def test_load_table_200(rest_mock: Mocker, example_table_metadata_with_snapshot_ assert actual == expected +def test_load_table_honor_access_delegation( + rest_mock: Mocker, example_table_metadata_with_snapshot_v1_rest_json: Dict[str, Any] +) -> None: + test_headers_with_remote_signing = {**TEST_HEADERS, "X-Iceberg-Access-Delegation": "remote-signing"} + rest_mock.get( + f"{TEST_URI}v1/namespaces/fokko/tables/table", + json=example_table_metadata_with_snapshot_v1_rest_json, + status_code=200, + request_headers=test_headers_with_remote_signing, + ) + # catalog = RestCatalog("rest", **{"uri": TEST_URI, "token": TEST_TOKEN, "access-delegation": "remote-signing"}) + catalog = RestCatalog( + "rest", + **{ + "uri": TEST_URI, + "token": TEST_TOKEN, + "header.X-Iceberg-Access-Delegation": "remote-signing", + }, + ) + actual = catalog.load_table(("fokko", "table")) + expected = Table( + identifier=("fokko", "table"), + metadata_location=example_table_metadata_with_snapshot_v1_rest_json["metadata-location"], + metadata=TableMetadataV1(**example_table_metadata_with_snapshot_v1_rest_json["metadata"]), + io=load_file_io(), + catalog=catalog, + ) + # First compare the dicts + assert actual.metadata.model_dump() == expected.metadata.model_dump() + assert actual == expected + + def test_load_table_from_self_identifier_200( rest_mock: Mocker, example_table_metadata_with_snapshot_v1_rest_json: Dict[str, Any] ) -> None: