Skip to content

Commit

Permalink
Merge pull request #453 from wri/gtc-2576b
Browse files Browse the repository at this point in the history
GTC-2576 Restrict queries on licensed WDPA dataset - merge to master
  • Loading branch information
danscales authored Dec 27, 2023
2 parents a2ca0da + 05880f7 commit 1a5002a
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 16 deletions.
28 changes: 20 additions & 8 deletions app/authentication/token.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
from typing import Tuple
from typing import Tuple, cast

from fastapi import Depends, HTTPException
from fastapi.logger import logger
from fastapi.security import OAuth2PasswordBearer
from httpx import Response
from ..routes import dataset_dependency

from ..utils.rw_api import who_am_i
from ..settings.globals import PROTECTED_QUERY_DATASETS

# token dependency where we immediately cause an exception if there is no auth token
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")

# token dependency where we don't cause exception if there is no auth token
oauth2_scheme_no_auto = OAuth2PasswordBearer(tokenUrl="/token", auto_error=False)

async def is_service_account(token: str = Depends(oauth2_scheme)) -> bool:
"""Calls GFW API to authorize user.
Expand Down Expand Up @@ -36,13 +40,21 @@ async def is_admin(token: str = Depends(oauth2_scheme)) -> bool:

return await is_app_admin(token, "gfw", "Unauthorized")

async def is_gfwpro_admin(error_str: str, token: str = Depends(oauth2_scheme)) -> bool:
"""Calls GFW API to authorize user.
User must be ADMIN for gfw pro app
async def is_gfwpro_admin_for_query(dataset: str = Depends(dataset_dependency),
token: str | None = Depends(oauth2_scheme_no_auto)) -> bool:
"""If the dataset is protected dataset, calls GFW API to authorize user by
requiring the user must be ADMIN for gfw-pro app. If the dataset is not
protected, just returns True without any required token or authorization.
"""

return await is_app_admin(token, "gfw-pro", error_str)

if dataset in PROTECTED_QUERY_DATASETS:
if token == None:
raise HTTPException(status_code=401, detail="Unauthorized query on a restricted dataset")
else:
return await is_app_admin(cast(str, token), "gfw-pro",
error_str="Unauthorized query on a restricted dataset")

return True

async def is_app_admin(token: str, app: str, error_str: str) -> bool:
"""Calls GFW API to authorize user.
Expand Down
12 changes: 5 additions & 7 deletions app/routes/datasets/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from pydantic.tools import parse_obj_as
from sqlalchemy.sql import and_

from ...authentication.token import is_gfwpro_admin
from ...authentication.token import is_gfwpro_admin_for_query
from ...application import db

# from ...authentication.api_keys import get_api_key
Expand Down Expand Up @@ -86,10 +86,6 @@
# Special suffixes to do an extra area density calculation on the raster data set.
AREA_DENSITY_RASTER_SUFFIXES = ["_ha-1", "_ha_yr-1"]

# Datasets that require admin privileges to do a query. (Extra protection on
# commercial datasets which shouldn't be downloaded in any way.)
PROTECTED_QUERY_DATASETS = ["wdpa_licensed_protected_areas"]

@router.get(
"/{dataset}/{version}/query",
response_class=RedirectResponse,
Expand Down Expand Up @@ -134,6 +130,7 @@ async def query_dataset_json(
geostore_origin: GeostoreOrigin = Query(
GeostoreOrigin.gfw, description="Service to search first for geostore."
),
is_authorized: bool = Depends(is_gfwpro_admin_for_query),
# api_key: APIKey = Depends(get_api_key),
):
"""Execute a READ-ONLY SQL query on the given dataset version (if
Expand All @@ -160,8 +157,6 @@ async def query_dataset_json(
"""

dataset, version = dataset_version
#if dataset in PROTECTED_QUERY_DATASETS:
# await is_gfwpro_admin(error_str="Unauthorized query on a restricted dataset")

if geostore_id:
geostore: Optional[GeostoreCommon] = await get_geostore(
Expand Down Expand Up @@ -197,6 +192,7 @@ async def query_dataset_csv(
delimiter: Delimiters = Query(
Delimiters.comma, description="Delimiter to use for CSV file."
),
is_authorized: bool = Depends(is_gfwpro_admin_for_query),
# api_key: APIKey = Depends(get_api_key),
):
"""Execute a READ-ONLY SQL query on the given dataset version (if
Expand Down Expand Up @@ -259,6 +255,7 @@ async def query_dataset_json_post(
*,
dataset_version: Tuple[str, str] = Depends(dataset_version_dependency),
request: QueryRequestIn,
is_authorized: bool = Depends(is_gfwpro_admin_for_query),
# api_key: APIKey = Depends(get_api_key),
):
"""Execute a READ-ONLY SQL query on the given dataset version (if
Expand Down Expand Up @@ -289,6 +286,7 @@ async def query_dataset_csv_post(
*,
dataset_version: Tuple[str, str] = Depends(dataset_version_dependency),
request: CsvQueryRequestIn,
is_authorized: bool = Depends(is_gfwpro_admin_for_query),
# api_key: APIKey = Depends(get_api_key),
):
"""Execute a READ-ONLY SQL query on the given dataset version (if
Expand Down
4 changes: 4 additions & 0 deletions app/settings/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,7 @@
GOOGLE_APPLICATION_CREDENTIALS = config(
"GOOGLE_APPLICATION_CREDENTIALS", cast=str, default="/root/.gcs/private_key.json"
)

# Datasets that require admin privileges to do a query. (Extra protection on
# commercial datasets which shouldn't be downloaded in any way.)
PROTECTED_QUERY_DATASETS = ["wdpa_licensed_protected_areas"]
1 change: 0 additions & 1 deletion tests_v2/unit/app/routes/datasets/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,6 @@ async def test_query_vector_asset_disallowed_10(
)

@pytest.mark.asyncio()
@pytest.mark.skip("Skip while figuring out permissions")
async def test_query_licensed_disallowed_11(
licensed_version, async_client: AsyncClient
):
Expand Down

0 comments on commit 1a5002a

Please sign in to comment.