From 1608f1c092eb2011ecaf8485257648cc7971b213 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 24 Apr 2023 14:37:18 +0300 Subject: oauth2: Add auth-checking wrapper to `/show_trait?...` page As a proof-of-concept, add some sort of wrapper to check whether the user has access to the given trait/dataset. This will probably need some improvement to check for edit access, curation access, etc. --- wqflask/wqflask/oauth2/client.py | 8 +++ wqflask/wqflask/templates/show_trait_error.html | 20 ++++++ wqflask/wqflask/views.py | 81 ++++++++++++++++++------- 3 files changed, 86 insertions(+), 23 deletions(-) create mode 100644 wqflask/wqflask/templates/show_trait_error.html diff --git a/wqflask/wqflask/oauth2/client.py b/wqflask/wqflask/oauth2/client.py index 1d3e07ae..dba52644 100644 --- a/wqflask/wqflask/oauth2/client.py +++ b/wqflask/wqflask/oauth2/client.py @@ -1,4 +1,5 @@ """Common oauth2 client utilities.""" +import requests from typing import Optional from urllib.parse import urljoin @@ -46,3 +47,10 @@ def oauth2_post( return Right(resp.json()) return Left(resp) + +def no_token_get(uri_path: str, **kwargs) -> Either: + config = app.config + resp = requests.get(urljoin(config["GN_SERVER_URL"], uri_path), **kwargs) + if resp.status_code == 200: + return Right(resp.json()) + return Left(resp) diff --git a/wqflask/wqflask/templates/show_trait_error.html b/wqflask/wqflask/templates/show_trait_error.html new file mode 100644 index 00000000..c924d1f1 --- /dev/null +++ b/wqflask/wqflask/templates/show_trait_error.html @@ -0,0 +1,20 @@ +{%extends "base.html"%} +{%block title%}Trait Data and Analysis{%endblock%} +{%block css%} + + + + + + + + + + + +{%endblock%} +{%block content%} +
+ {{flash_me()}} +
+{%endblock%} diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py index 90747fc2..f14ccdd0 100644 --- a/wqflask/wqflask/views.py +++ b/wqflask/wqflask/views.py @@ -38,6 +38,8 @@ from flask import send_from_directory from flask import redirect from flask import send_file from flask import url_for +from flask import flash +from flask import session # Some of these (like collect) might contain endpoints, so they're still used. # Blueprints should probably be used instead. @@ -75,6 +77,9 @@ from wqflask.docs import Docs, update_text from wqflask.decorators import edit_access_required from wqflask.db_info import InfoPage +from wqflask.oauth2.client import no_token_get +from wqflask.oauth2.request_utils import process_error + from utility import temp_data from utility.tools import TEMPDIR from utility.tools import USE_REDIS @@ -482,30 +487,60 @@ def show_temp_trait_page(): @app.route("/show_trait") def show_trait_page(): - with database_connection() as conn, conn.cursor() as cursor: - user_id = ((g.user_session.record.get(b"user_id") or b"").decode("utf-8") - or g.user_session.record.get("user_id") or "") - template_vars = show_trait.ShowTrait(cursor, - user_id=user_id, - kw=request.args) - template_vars.js_data = json.dumps(template_vars.js_data, - default=json_default_handler, - indent=" ") - # Should there be any mis-configurations, things will still - # work. - metadata = {} - try: - metadata = requests.get( - urljoin( - GN3_LOCAL_URL, - f"/api/metadata/dataset/{request.args.get('dataset')}") - ).json() - except: + def __show_trait__(): + with database_connection() as conn, conn.cursor() as cursor: + + user_id = ((g.user_session.record.get(b"user_id") or b"").decode("utf-8") + or g.user_session.record.get("user_id") or "") + template_vars = show_trait.ShowTrait(cursor, + user_id=user_id, + kw=request.args) + template_vars.js_data = json.dumps(template_vars.js_data, + default=json_default_handler, + indent=" ") + # Should there be any mis-configurations, things will still + # work. metadata = {} - return render_template( - "show_trait.html", - metadata=metadata, - **template_vars.__dict__) + try: + metadata = requests.get( + urljoin( + GN3_LOCAL_URL, + f"/api/metadata/dataset/{request.args.get('dataset')}") + ).json() + except: + metadata = {} + return render_template( + "show_trait.html", + metadata=metadata, + **template_vars.__dict__) + dataset = request.args["dataset"] + trait_id = request.args["trait_id"] + def __failure__(err): + error = process_error(err) + flash(f"{error['error']}: {error['error_description']}", "alert-error") + return render_template("show_trait_error.html") + + def __success__(auth_results): + trait_privileges = auth_results[0]["privileges"] + if ("group:resource:view-resource" in trait_privileges or + "system:resource:public-read" in trait_privileges): + return __show_trait__() + flash( + f"AuthorisationError: You do not have access to trait '{trait_id}' " + f"from the '{dataset}' dataset.", + "alert-danger") + return render_template("show_trait_error.html") + + return no_token_get( + "oauth2/data/authorisation", + headers={ + "Content-Type": "application/json", + **({"Authorization": f"Bearer {session['token']}"} + if bool(session.get("token")) else {}) + }, + json={ + "traits": [f"{dataset}::{trait_id}"] + }).either(__failure__, __success__) @app.route("/heatmap", methods=('POST',)) -- cgit v1.2.3