Skip to content

Commit

Permalink
Merge pull request #108 from bento-platform/features/anon-discovery-c…
Browse files Browse the repository at this point in the history
…onfig-only

anonymous metadata queries should use discovery config only
  • Loading branch information
gsfk authored Sep 16, 2024
2 parents d78d2ea + bd49a6c commit b3b8b78
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 14 deletions.
3 changes: 2 additions & 1 deletion bento_beacon/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from .utils.beacon_request import save_request_data, validate_request, verify_permissions
from .utils.beacon_response import init_response_data
from .utils.katsu_utils import katsu_censorship_settings
from .utils.censorship import set_censorship_settings
from .utils.censorship import set_censorship_settings, reject_query_if_not_permitted

REQUEST_SPEC_RELATIVE_PATH = "beacon-v2/framework/json/requests/"
BEACON_MODELS = ["analyses", "biosamples", "cohorts", "datasets", "individuals", "runs", "variants"]
Expand Down Expand Up @@ -96,6 +96,7 @@ def before_request():
validate_request()
verify_permissions()
save_request_data()
reject_query_if_not_permitted()
init_response_data()


Expand Down
3 changes: 3 additions & 0 deletions bento_beacon/config_files/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ class Config:
PHENOPACKETS_SCHEMA_REFERENCE = {"entityType": "individual", "schema": "phenopackets v1"}

MAX_RETRIES_FOR_CENSORSHIP_PARAMS = 2

# don't let anonymous users query arbitrary phenopacket or experiment fields
ANONYMOUS_METADATA_QUERY_USES_DISCOVERY_CONFIG_ONLY = True
# -------------------
# gohan

Expand Down
12 changes: 6 additions & 6 deletions bento_beacon/endpoints/individuals.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@
P_DOWNLOAD_DATA,
P_QUERY_DATA,
)
from flask import Blueprint
from flask import Blueprint, g
from functools import reduce
from ..authz.middleware import authz_middleware, check_permission
from ..utils.beacon_request import (
query_parameters_from_request,
summary_stats_requested,
)
from ..utils.beacon_request import summary_stats_requested
from ..utils.beacon_response import (
add_info_to_response,
add_stats_to_response,
Expand All @@ -32,7 +29,10 @@

@individuals.route("/individuals", methods=["GET", "POST"])
def get_individuals():
variants_query, phenopacket_filters, experiment_filters, config_filters = query_parameters_from_request()
variants_query = g.beacon_query_parameters["variants_query"]
phenopacket_filters = g.beacon_query_parameters["phenopacket_filters"]
experiment_filters = g.beacon_query_parameters["experiment_filters"]
config_filters = g.beacon_query_parameters["config_filters"]

no_query = not (variants_query or phenopacket_filters or experiment_filters or config_filters)
search_sample_ids = variants_query or experiment_filters
Expand Down
8 changes: 5 additions & 3 deletions bento_beacon/endpoints/variants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from flask import Blueprint
from flask import Blueprint, g
from ..authz.middleware import authz_middleware
from ..utils.beacon_request import query_parameters_from_request
from ..utils.beacon_response import build_query_response, add_info_to_response, zero_count_response
from ..utils.gohan_utils import query_gohan, gohan_total_variants_count, gohan_totals_by_sample_id
from ..utils.search import biosample_id_search
Expand All @@ -12,7 +11,10 @@
@variants.route("/g_variants", methods=["GET", "POST"])
@authz_middleware.deco_public_endpoint # TODO: for now. eventually, return more depending on permissions
def get_variants():
variants_query, phenopacket_filters, experiment_filters, config_filters = query_parameters_from_request()
variants_query = g.beacon_query_parameters["variants_query"]
phenopacket_filters = g.beacon_query_parameters["phenopacket_filters"]
experiment_filters = g.beacon_query_parameters["experiment_filters"]
config_filters = g.beacon_query_parameters["config_filters"]
has_filters = phenopacket_filters or experiment_filters or config_filters

# if no query, return total count of variants
Expand Down
17 changes: 13 additions & 4 deletions bento_beacon/utils/beacon_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ def expand_path(id):
return id.replace("/", ".[item].")


def query_parameters_from_request():
variants_query = g.request_data.get("requestParameters", {}).get("g_variant") or {}
filters = g.request_data.get("filters") or []
def parse_query_params(request_data):
variants_query = request_data.get("requestParameters", {}).get("g_variant") or {}
filters = request_data.get("filters") or []
phenopacket_filters = list(filter(lambda f: f["id"].startswith("phenopacket."), filters))
experiment_filters = list(filter(lambda f: f["id"].startswith("experiment."), filters))
config_filters = [f for f in filters if f not in phenopacket_filters and f not in experiment_filters]
Expand All @@ -49,7 +49,12 @@ def query_parameters_from_request():
experiment_filters,
)
)
return variants_query, phenopacket_filters, experiment_filters, config_filters
return {
"variants_query": variants_query,
"phenopacket_filters": phenopacket_filters,
"experiment_filters": experiment_filters,
"config_filters": config_filters,
}


# structure GET params so they match the nested structure in POST
Expand Down Expand Up @@ -130,8 +135,12 @@ def save_request_data():
if request_bento:
request_data["bento"] = request_bento

# raw request data, this is echoed in response "meta" field
g.request_data = request_data

# parsed query components
g.beacon_query_parameters = parse_query_params(request_data)


def validate_request():
if request.method == "POST":
Expand Down
16 changes: 16 additions & 0 deletions bento_beacon/utils/censorship.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,19 @@ def reject_if_too_many_filters(filters):
def censored_chart_data(data):
t = get_censorship_threshold() # zero with correct permissions
return [{"label": d["label"], "value": d["value"]} for d in data if d["value"] > t]


def query_has_phenopacket_filter():
return bool(g.beacon_query_parameters["phenopacket_filters"])


def query_has_experiment_filter():
return bool(g.beacon_query_parameters["experiment_filters"])


# some anonymous queries are not permitted
def reject_query_if_not_permitted():
if g.permission_query_data or not current_app.config["ANONYMOUS_METADATA_QUERY_USES_DISCOVERY_CONFIG_ONLY"]:
return
if query_has_phenopacket_filter() or query_has_experiment_filter():
raise InvalidQuery("anonymous queries should use filters from discovery config only")

0 comments on commit b3b8b78

Please sign in to comment.