Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions stac_fastapi/core/stac_fastapi/core/private_fields_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"""Configuration for private fields that should be excluded from API responses."""
PRIVATE_FIELDS = ["product_location"]
23 changes: 23 additions & 0 deletions stac_fastapi/core/stac_fastapi/core/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,30 @@
from stac_fastapi.types import stac as stac_types
from stac_fastapi.types.links import ItemLinks, resolve_links

from .private_fields_config import PRIVATE_FIELDS

logger = logging.getLogger(__name__)


def filter_private_fields(data: dict) -> dict:
"""Remove private fields from data dictionary."""
if not PRIVATE_FIELDS:
return data

filtered_data = deepcopy(data)

for field in PRIVATE_FIELDS:
if field in filtered_data:
del filtered_data[field]

if "properties" in filtered_data and isinstance(filtered_data["properties"], dict):
for field in PRIVATE_FIELDS:
if field in filtered_data["properties"]:
del filtered_data["properties"][field]

return filtered_data


@attr.s
class Serializer(abc.ABC):
"""Defines serialization methods between the API and the data model.
Expand Down Expand Up @@ -92,6 +113,7 @@ def db_to_stac(cls, item: dict, base_url: str) -> stac_types.Item:
Returns:
stac_types.Item: The STAC item object.
"""
item = filter_private_fields(item)
item_id = item["id"]
collection_id = item["collection"]
item_links = ItemLinks(
Expand Down Expand Up @@ -168,6 +190,7 @@ def db_to_stac(
Returns:
stac_types.Collection: The STAC collection object.
"""
collection = filter_private_fields(collection)
# Avoid modifying the input dict in-place ... doing so breaks some tests
collection = deepcopy(collection)

Expand Down
33 changes: 33 additions & 0 deletions stac_fastapi/tests/resources/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,36 @@ async def test_links_collection(app_client, ctx, txn_client):
len([link for link in response.json()["links"] if link["rel"] == "license"])
== 1
)


@pytest.mark.asyncio
async def test_private_fields_excluded_from_collection(
app_client, txn_client, load_test_data, monkeypatch
):
"""Test that private fields are excluded from collection API responses."""
monkeypatch.setattr(
"stac_fastapi.core.serializers.PRIVATE_FIELDS", ["test_field", "internal_data"]
)

test_collection = load_test_data("test_collection.json")
test_collection["id"] = "test-private-fields-collection"

test_collection["test_field"] = "this should be hidden in collection"
test_collection["internal_data"] = {"secret": "confidential collection info"}
test_collection["description"] = "This should be visible"

await create_collection(txn_client, test_collection)
await refresh_indices(txn_client)

resp = await app_client.get(f"/collections/{test_collection['id']}")
assert resp.status_code == 200

collection_data = resp.json()

assert "test_field" not in collection_data
assert "internal_data" not in collection_data

assert "description" in collection_data
assert collection_data["description"] == "This should be visible"
assert "id" in collection_data
assert "title" in collection_data
36 changes: 36 additions & 0 deletions stac_fastapi/tests/resources/test_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -1163,3 +1163,39 @@ async def test_search_datetime_with_null_datetime(
await txn_client.delete_collection(test_collection["id"])
except Exception as e:
logger.warning(f"Failed to delete collection: {e}")


@pytest.mark.asyncio
async def test_private_fields_excluded_from_item(
app_client, txn_client, load_test_data, monkeypatch
):
"""Test that private fields are excluded from item API responses."""
monkeypatch.setattr(
"stac_fastapi.core.serializers.PRIVATE_FIELDS", ["test_field", "internal_data"]
)

test_collection = load_test_data("test_collection.json")
test_item = load_test_data("test_item.json")

test_collection["id"] = "test-private-fields-collection"
test_item["collection"] = test_collection["id"]
test_item["id"] = "test-private-fields-item"

test_item["test_field"] = "test field to be hidden"
test_item["internal_data"] = {"secret": "confidential"}

await create_collection(txn_client, test_collection)

await create_item(txn_client, test_item)
await refresh_indices(txn_client)

resp = await app_client.get(
f"/collections/{test_item['collection']}/items/{test_item['id']}"
)
item_data = resp.json()

assert "test_field" not in item_data
assert "internal_data" not in item_data

assert "id" in item_data
assert item_data["id"] == "test-private-fields-item"