Skip to content

Commit 23e998f

Browse files
Yuri ZmytrakovYuri Zmytrakov
authored andcommitted
fix: Adding a key for technical/private data in SFEOS
1 parent f834a9e commit 23e998f

File tree

3 files changed

+90
-2
lines changed

3 files changed

+90
-2
lines changed

stac_fastapi/core/stac_fastapi/core/serializers.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from stac_fastapi.core.datetime_utils import now_to_rfc3339_str
1212
from stac_fastapi.core.models.links import CollectionLinks
13-
from stac_fastapi.core.utilities import get_bool_env
13+
from stac_fastapi.core.utilities import get_bool_env, hide_private_data
1414
from stac_fastapi.types import stac as stac_types
1515
from stac_fastapi.types.links import ItemLinks, resolve_links
1616

@@ -108,7 +108,7 @@ def db_to_stac(cls, item: dict, base_url: str) -> stac_types.Item:
108108
else:
109109
assets = item.get("assets", {})
110110

111-
return stac_types.Item(
111+
stac_item = stac_types.Item(
112112
type="Feature",
113113
stac_version=item.get("stac_version", ""),
114114
stac_extensions=item.get("stac_extensions", []),
@@ -121,6 +121,12 @@ def db_to_stac(cls, item: dict, base_url: str) -> stac_types.Item:
121121
assets=assets,
122122
)
123123

124+
if get_bool_env("HIDE_PRIVATE_DATA"):
125+
fields_to_remove = ["private_data"]
126+
stac_item = hide_private_data(stac_item, fields_to_remove)
127+
128+
return stac_item
129+
124130

125131
class CollectionSerializer(Serializer):
126132
"""Serialization methods for STAC collections."""

stac_fastapi/core/stac_fastapi/core/utilities.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
such as converting bounding boxes to polygon representations.
55
"""
66

7+
import copy
78
import logging
89
import os
910
from typing import Any, Dict, List, Optional, Set, Union
@@ -178,3 +179,34 @@ def dict_deep_update(merge_to: Dict[str, Any], merge_from: Dict[str, Any]) -> No
178179
dict_deep_update(merge_to[k], merge_from[k])
179180
else:
180181
merge_to[k] = v
182+
183+
184+
def hide_private_data(obj: Any, private_field_names: Union[str, List[str]]) -> Any:
185+
"""Hide private data fields from the object.
186+
187+
Args:
188+
obj: The object to process (dict, list, or other)
189+
private_field_names: A list or single field names to hide from response
190+
"""
191+
obj = copy.deepcopy(obj)
192+
193+
field_names = (
194+
[private_field_names]
195+
if isinstance(private_field_names, str)
196+
else private_field_names
197+
)
198+
199+
if not field_names:
200+
return obj
201+
202+
if isinstance(obj, dict):
203+
for field in field_names:
204+
obj.pop(field, None)
205+
for value in obj.values():
206+
hide_private_data(value, field_names)
207+
208+
elif isinstance(obj, list):
209+
for item in obj:
210+
hide_private_data(item, field_names)
211+
212+
return obj

stac_fastapi/tests/api/test_api.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,3 +1657,53 @@ async def test_use_datetime_false(app_client, load_test_data, txn_client, monkey
16571657

16581658
assert "test-item-datetime-only" not in found_ids
16591659
assert "test-item-start-end-only" in found_ids
1660+
1661+
1662+
@pytest.mark.asyncio
1663+
async def test_hide_private_data_true(app_client, txn_client, load_test_data):
1664+
"""Test hiding the private data from response"""
1665+
1666+
os.environ["HIDE_PRIVATE_DATA"] = "true"
1667+
1668+
test_collection = load_test_data("test_collection.json")
1669+
test_collection_id = "test-collection-hide-private-data"
1670+
test_collection["id"] = test_collection_id
1671+
await create_collection(txn_client, test_collection)
1672+
1673+
test_item = load_test_data("test_item.json")
1674+
test_item_id = "test-item-hide-private-data"
1675+
test_item["id"] = test_item_id
1676+
test_item["collection"] = test_collection_id
1677+
test_item["private_data"] = {"item_secret": "sensitive_info"}
1678+
await create_item(txn_client, test_item)
1679+
1680+
# Test /collections/{collection_id}/items
1681+
resp = await app_client.get(f"/collections/{test_collection_id}/items")
1682+
assert resp.status_code == 200
1683+
resp_json = resp.json()
1684+
item = resp_json["features"][0]
1685+
assert "private_data" not in item
1686+
1687+
# Test /collections/{collection_id}/items/{item_id}
1688+
resp = await app_client.get(
1689+
f"/collections/{test_collection_id}/items/{test_item_id}"
1690+
)
1691+
assert resp.status_code == 200
1692+
resp_json = resp.json()
1693+
assert "private_data" not in resp_json
1694+
1695+
# Test GET /search
1696+
resp = await app_client.get(f"/search?collections={test_collection_id}")
1697+
assert resp.status_code == 200
1698+
resp_json = resp.json()
1699+
item = resp_json["features"][0]
1700+
assert "private_data" not in item
1701+
1702+
# Test POST /search
1703+
resp = await app_client.post("/search", json={"collections": [test_collection_id]})
1704+
assert resp.status_code == 200
1705+
resp_json = resp.json()
1706+
item = resp_json["features"][0]
1707+
assert "private_data" not in item
1708+
1709+
del os.environ["HIDE_PRIVATE_DATA"]

0 commit comments

Comments
 (0)