about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2023-04-24 14:37:18 +0300
committerFrederick Muriuki Muriithi2023-05-08 10:08:17 +0300
commit1608f1c092eb2011ecaf8485257648cc7971b213 (patch)
tree362557b80f39c09fcbcf18eb374922619f23ab74
parent5a8432adb56f9fddbc9e42a9eb8de5e4a0879ee6 (diff)
downloadgenenetwork2-1608f1c092eb2011ecaf8485257648cc7971b213.tar.gz
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.
-rw-r--r--wqflask/wqflask/oauth2/client.py8
-rw-r--r--wqflask/wqflask/templates/show_trait_error.html20
-rw-r--r--wqflask/wqflask/views.py81
3 files changed, 86 insertions, 23 deletions
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%}
+<link rel="stylesheet" type="text/css" href="/static/new/css/bar_chart.css" />
+<link rel="stylesheet" type="text/css" href="/static/new/css/box_plot.css" />
+<link rel="stylesheet" type="text/css" href="/static/new/css/prob_plot.css" />
+<link rel="stylesheet" type="text/css" href="/static/new/css/scatter-matrix.css" />
+<link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='d3-tip/d3-tip.css') }}" />
+<link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTables/css/jquery.dataTables.css') }}" />
+<link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='nouislider/nouislider.min.css') }}" />
+<link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" />
+<link rel="stylesheet" type="text/css" href="/static/new/css/trait_list.css" />
+<link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
+
+{%endblock%}
+{%block content%} <!-- Start of body -->
+<div class="container">
+  {{flash_me()}}
+</div>
+{%endblock%} <!-- End of body -->
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',))