Skip to content

Commit

Permalink
Merge pull request #44 from bento-platform/chore/authz-for-public
Browse files Browse the repository at this point in the history
integrate public runs endpoint to authz'd WES
  • Loading branch information
davidlougheed authored Aug 16, 2023
2 parents 917eee7 + c9e04b9 commit c1929fe
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 10 deletions.
2 changes: 1 addition & 1 deletion bento_wes/backends/_wes_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ def _initialize_run_and_get_command(
f"{workflow_id}.project_id": run.request.tags.project_id,
f"{workflow_id}.dataset_id": run.request.tags.dataset_id,
# Don't use data_type from workflow metadata here - instead, workflows can say what they're ingesting

f"{workflow_id}.service_url": run.request.tags.service_url,
# TODO: more special parameters: service URLs, system__run_dir...
}
Expand Down
1 change: 1 addition & 0 deletions bento_wes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

__all__ = [
"BentoWorkflowInput",
"BentoWorkflowInputWithValue",
"BentoWorkflowOutput",
"BentoWorkflowMetadata",
"BentoRunRequestTags",
Expand Down
55 changes: 48 additions & 7 deletions bento_wes/runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from .logger import logger
from .models import RunRequest
from .runner import run_workflow
from .states import STATE_COMPLETE
from .types import RunStream
from .workflows import (
WorkflowType,
Expand All @@ -47,7 +48,7 @@
def _check_runs_permission(run_requests: list[RunRequest], permission: str) -> tuple[bool, ...]:
if not current_app.config["AUTHZ_ENABLED"]:
return tuple([True] * len(run_requests)) # Assume we have permission for everything if authz disabled

authz_response = authz_middleware.authz_post(request, "/policy/evaluate", body={
"requested_resource": [
{
Expand Down Expand Up @@ -183,6 +184,31 @@ def _create_run(db: sqlite3.Connection, c: sqlite3.Cursor) -> Response:
return jsonify({"run_id": str(run_id)})


PUBLIC_RUN_DETAILS_SHAPE = {
"request": {
"workflow_type": True,
"tags": {
"workflow_id": True,
"workflow_metadata": {
"data_type": True,
},
"project_id": True,
"dataset_id": True,
},
},
"run_log": {
"start_time": True,
"end_time": True,
},
}

PRIVATE_RUN_DETAILS_SHAPE = {
"request": True,
"run_log": True,
"task_logs": True,
}


@bp_runs.route("/runs", methods=["GET", "POST"])
def run_list():
db = get_db()
Expand All @@ -200,6 +226,8 @@ def run_list():
return flask_bad_request_error("Value error")

# GET
# Bento Extension: Include run public details with /runs request
public_endpoint = request.args.get("public", "false").lower() == "true"
# Bento Extension: Include run details with /runs request
with_details = request.args.get("with_details", "false").lower() == "true"

Expand All @@ -209,13 +237,26 @@ def run_list():
for r in c.execute("SELECT * FROM runs").fetchall():
run = run_with_details_and_output_from_row(c, r, stream_content=False)
perms_list.append(run.request)
res_list.append({
**run.model_dump(mode="json", include={"run_id", "state"}),
**({"details": run.model_dump(mode="json", exclude={"outputs"})} if with_details else {}),
})

p_res = _check_runs_permission(perms_list, PERMISSION_VIEW_RUNS)
res_list = [v for v, p in zip(res_list, p_res) if p]
if not public_endpoint or run.state == STATE_COMPLETE:
res_list.append({
**run.model_dump(mode="json", include={"run_id", "state"}),
**(
{
"details": run.model_dump(mode="json", include={
"run_id": True,
"state": True,
**(PUBLIC_RUN_DETAILS_SHAPE if public_endpoint else PRIVATE_RUN_DETAILS_SHAPE),
}),
}
if with_details else {}
),
})

if not public_endpoint:
# Filter runs to just those which we have permission to view
p_res = _check_runs_permission(perms_list, PERMISSION_VIEW_RUNS)
res_list = [v for v, p in zip(res_list, p_res) if p]

authz_middleware.mark_authz_done(request)

Expand Down
1 change: 1 addition & 0 deletions tests/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
},
"project_id": EXAMPLE_PROJECT_ID,
"dataset_id": EXAMPLE_DATASET_ID,
"service_url": "http://metadata.local",
},
}

Expand Down
47 changes: 45 additions & 2 deletions tests/test_runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import responses
import uuid

from bento_wes.states import STATE_QUEUED

from .constants import EXAMPLE_RUN, EXAMPLE_RUN_BODY

from bento_wes.states import STATE_QUEUED, STATE_COMPLETE


def _add_workflow_response(r):
with open(os.path.join(os.path.dirname(__file__), "phenopackets_json.wdl"), "r") as wf:
Expand Down Expand Up @@ -168,3 +168,46 @@ def test_run_cancel_endpoint(client, mocked_responses):
# error = rv.get_json()
# assert len(error["errors"]) == 1
# assert error["errors"][0]["message"] == "Run already canceled"


def test_runs_public_endpoint(client, mocked_responses):
from bento_wes.db import get_db, update_run_state_and_commit
from bento_lib.events import EventBus

event_bus = EventBus(allow_fake=True) # mock event bus

_add_workflow_response(mocked_responses)

# first, create a run, so we have something to fetch
rv = client.post("/runs", data=EXAMPLE_RUN_BODY)
assert rv.status_code == 200 # 200 is WES spec, even though 201 would be better (?)

# make sure the run is complete, otherwise the public endpoint won't list it
db = get_db()
c = db.cursor()
update_run_state_and_commit(db, c, rv.get_json()["run_id"], STATE_COMPLETE, event_bus)

# validate the public runs endpoint
rv = client.get("/runs?with_details=true&public=true")
assert rv.status_code == 200
data = rv.get_json()

expected_keys = ["run_id", "state", "details"]
expected_details_keys = ["request", "run_id", "run_log", "state"]
expected_request_keys = ["tags", "workflow_type"]
expected_tags_keys = ["workflow_id", "workflow_metadata", "project_id", "dataset_id"]
expected_metadata_keys = ["data_type"]
expected_run_log_keys = ["end_time", "start_time"]

for run in data:
assert set(run.keys()) == set(expected_keys)
details = run["details"]
assert set(details.keys()) == set(expected_details_keys)
request = details["request"]
assert set(request.keys()) == set(expected_request_keys)
tags = request["tags"]
assert set(tags.keys()) == set(expected_tags_keys)
metadata = tags["workflow_metadata"]
assert set(metadata.keys()) == set(expected_metadata_keys)
run_log = details["run_log"]
assert set(run_log.keys()) == set(expected_run_log_keys)

0 comments on commit c1929fe

Please sign in to comment.