Cameras: redact API keys from feed errors; gate TfL/Calgary; clamp map bbox
- Feed/catalog error text (shown to every signed-in user via GET /cameras and
/traffic/511 status fields) carried httpx's full request URL, including
?key= for Ontario 511 and the 511NY DAA key. Query strings are now dropped.
- TfL and Calgary licences are per GEV only (TfL's terms page 403'd), the same
footing as Ontario 511, so they are UNVERIFIED (acknowledgement required)
instead of ungated attribution_required. Attribution still renders.
- The map camera layer clamps its bbox to +/-180/+/-90; zoomed-out bounds
produced a 422 ("could not reach server").
Refs server-26#198
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e6b1581139
commit
2f21728bce
@@ -111,6 +111,18 @@ def test_gated_licence_requires_acknowledgement(store):
|
||||
assert store.audit[-1]["details"]["provider_id"] == "ny511"
|
||||
|
||||
|
||||
def test_licences_not_read_first_hand_are_gated_but_keep_attribution():
|
||||
# TfL (terms page 403'd) and Calgary (per GEV only) are not confirmed, so an
|
||||
# admin must acknowledge before they serve; their credit still renders.
|
||||
for pid in ("tfl", "calgary", "ontario511"):
|
||||
lic = registry.PROVIDERS[pid].meta.licence
|
||||
assert lic.status == Licence.UNVERIFIED and lic.gated, pid
|
||||
assert registry.PROVIDERS["tfl"].meta.licence.attribution == "Powered by TfL Open Data"
|
||||
with pytest.raises(registry.ProviderError) as e:
|
||||
run(registry.set_enabled("tfl", True, ("u", "e")))
|
||||
assert e.value.extra["requires_acknowledgement"] is True
|
||||
|
||||
|
||||
def test_gated_enable_refused_when_audit_cannot_be_written(store):
|
||||
with patch.object(registry, "_audit", AsyncMock(side_effect=RuntimeError("firestore down"))):
|
||||
with pytest.raises(registry.ProviderError) as e:
|
||||
@@ -141,7 +153,7 @@ def test_unreadable_settings_fail_closed():
|
||||
|
||||
def test_query_returns_attribution_and_skips_providers_outside_bbox(store):
|
||||
run(registry.set_enabled("nsw", True, ("u", "e")))
|
||||
run(registry.set_enabled("tfl", True, ("u", "e")))
|
||||
run(registry.set_enabled("tfl", True, ("u", "e"), acknowledge_licence=True))
|
||||
cam = base.make_camera("nsw", "x", -33.87, 151.2, name="Sydney", image_url="https://webcams.transport.nsw.gov.au/a.jpg")
|
||||
nsw_fetch, tfl_fetch = AsyncMock(return_value=[cam]), AsyncMock(return_value=[])
|
||||
with patch.object(registry.PROVIDERS["nsw"], "fetch_catalog", nsw_fetch), \
|
||||
@@ -277,6 +289,18 @@ def test_ny511_provider_wraps_legacy_client_and_carries_daa_metadata():
|
||||
assert "with all faults" in lic.disclaimer and lic.gated
|
||||
|
||||
|
||||
def test_refresh_error_never_echoes_an_api_key():
|
||||
import httpx
|
||||
req = httpx.Request("GET", "https://511on.ca/api/v2/get/cameras", params={"format": "json", "key": "SECRET123"})
|
||||
with pytest.raises(httpx.HTTPStatusError) as raised: # httpx's own message carries the full URL
|
||||
httpx.Response(403, request=req).raise_for_status()
|
||||
err = raised.value
|
||||
cat = registry.CATALOGS["ontario511"]
|
||||
with patch.object(cat.provider, "fetch_catalog", AsyncMock(side_effect=err)):
|
||||
run(cat.get())
|
||||
assert "SECRET123" not in cat.error and "403" in cat.error and "511on.ca/api/v2/get/cameras" in cat.error
|
||||
|
||||
|
||||
def test_failed_refresh_keeps_last_good_catalog():
|
||||
cat = registry.CATALOGS["nsw"]
|
||||
cam = base.make_camera("nsw", "x", -33.8, 151.2)
|
||||
|
||||
Reference in New Issue
Block a user