Skip to content

Commit 0ff7bcc

Browse files
authored
Merge pull request #547 from DataIntegrationGroup/jir-ogc
feat: refactor location CTE for materialized views and enhance path validation
2 parents 32237e3 + 9fc0b16 commit 0ff7bcc

2 files changed

Lines changed: 31 additions & 23 deletions

File tree

alembic/versions/d5e6f7a8b9c0_create_pygeoapi_supporting_views.py

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@
4545
("test_wells", "test well"),
4646
]
4747

48+
LATEST_LOCATION_CTE = """
49+
SELECT DISTINCT ON (lta.thing_id)
50+
lta.thing_id,
51+
lta.location_id,
52+
lta.effective_start
53+
FROM location_thing_association AS lta
54+
WHERE lta.effective_end IS NULL
55+
ORDER BY lta.thing_id, lta.effective_start DESC
56+
""".strip()
57+
4858

4959
def _safe_view_id(view_id: str) -> str:
5060
if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", view_id):
@@ -58,13 +68,7 @@ def _create_thing_view(view_id: str, thing_type: str) -> str:
5868
return f"""
5969
CREATE VIEW ogc_{safe_view_id} AS
6070
WITH latest_location AS (
61-
SELECT DISTINCT ON (lta.thing_id)
62-
lta.thing_id,
63-
lta.location_id,
64-
lta.effective_start
65-
FROM location_thing_association AS lta
66-
WHERE lta.effective_end IS NULL
67-
ORDER BY lta.thing_id, lta.effective_start DESC
71+
{LATEST_LOCATION_CTE}
6872
)
6973
SELECT
7074
t.id,
@@ -94,16 +98,10 @@ def _create_thing_view(view_id: str, thing_type: str) -> str:
9498

9599

96100
def _create_latest_depth_view() -> str:
97-
return """
101+
return f"""
98102
CREATE MATERIALIZED VIEW ogc_latest_depth_to_water_wells AS
99103
WITH latest_location AS (
100-
SELECT DISTINCT ON (lta.thing_id)
101-
lta.thing_id,
102-
lta.location_id,
103-
lta.effective_start
104-
FROM location_thing_association AS lta
105-
WHERE lta.effective_end IS NULL
106-
ORDER BY lta.thing_id, lta.effective_start DESC
104+
{LATEST_LOCATION_CTE}
107105
),
108106
ranked_obs AS (
109107
SELECT
@@ -147,16 +145,10 @@ def _create_latest_depth_view() -> str:
147145

148146

149147
def _create_avg_tds_view() -> str:
150-
return """
148+
return f"""
151149
CREATE MATERIALIZED VIEW ogc_avg_tds_wells AS
152150
WITH latest_location AS (
153-
SELECT DISTINCT ON (lta.thing_id)
154-
lta.thing_id,
155-
lta.location_id,
156-
lta.effective_start
157-
FROM location_thing_association AS lta
158-
WHERE lta.effective_end IS NULL
159-
ORDER BY lta.thing_id, lta.effective_start DESC
151+
{LATEST_LOCATION_CTE}
160152
),
161153
tds_obs AS (
162154
SELECT

core/pygeoapi.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import re
23
import textwrap
34
from importlib.util import find_spec
45
from pathlib import Path
@@ -182,6 +183,21 @@ def _mount_path() -> str:
182183

183184
# Remove any trailing slashes so "/ogcapi/" and "ogcapi/" both become "/ogcapi".
184185
path = path.rstrip("/")
186+
187+
# Disallow traversal/current-directory segments.
188+
segments = [segment for segment in path.split("/") if segment]
189+
if any(segment in {".", ".."} for segment in segments):
190+
raise ValueError(
191+
"Invalid PYGEOAPI_MOUNT_PATH: traversal segments are not allowed."
192+
)
193+
194+
# Allow only slash-delimited segments of alphanumerics, underscore, or hyphen.
195+
if not re.fullmatch(r"/[A-Za-z0-9_-]+(?:/[A-Za-z0-9_-]+)*", path):
196+
raise ValueError(
197+
"Invalid PYGEOAPI_MOUNT_PATH: only letters, numbers, underscores, "
198+
"hyphens, and slashes are allowed."
199+
)
200+
185201
return path
186202

187203

0 commit comments

Comments
 (0)