about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2023-10-10 11:11:16 +0300
committerFrederick Muriuki Muriithi2023-10-10 11:12:50 +0300
commit0171cea9b33fea798739971178078d9a1c0e3c88 (patch)
tree6901eba54f68735c90895fec445bcc9e942af53e
parentdf0ac11ff12d1f4c0176ef86f06ce4c7ac26c99e (diff)
downloadgenenetwork3-0171cea9b33fea798739971178078d9a1c0e3c88.tar.gz
Do authorisation checks for relevant functions
Add a `required_access(...)` function to be used to check that the user has
the appropriate privileges to act on the case attributes.
-rw-r--r--gn3/case_attributes.py69
-rw-r--r--gn3/settings.py1
2 files changed, 58 insertions, 12 deletions
diff --git a/gn3/case_attributes.py b/gn3/case_attributes.py
index 695298a..eadd3b8 100644
--- a/gn3/case_attributes.py
+++ b/gn3/case_attributes.py
@@ -2,12 +2,15 @@
 import os
 import csv
 import json
+import requests
 import tempfile
 from pathlib import Path
 from functools import reduce
 from datetime import datetime
+from urllib.parse import urljoin
 
 from MySQLdb.cursors import DictCursor
+from authlib.integrations.flask_oauth2.errors import _HTTPException
 from flask import jsonify, request, Response, Blueprint, current_app
 
 from gn3.commands import run_cmd
@@ -18,6 +21,8 @@ from gn3.auth.authorisation.users import User
 from gn3.auth.authorisation.errors import AuthorisationError
 from gn3.auth.authorisation.oauth2.resource_server import require_oauth
 
+from gn3.debug import __pk__
+
 caseattr = Blueprint("case-attribute", __name__)
 
 CATTR_DIFFS_DIR = "case-attribute-diffs"
@@ -29,6 +34,40 @@ class NoDiffError(ValueError):
         super().__init__(
             self, "No difference between existing data and sent data.")
 
+def required_access(inbredset_id: int, access_levels: tuple[str, ...]) -> bool:
+    """Check whether the user has the appropriate access"""
+    def __species_id__(conn):
+        with conn.cursor() as cursor:
+            cursor.execute(
+                "SELECT SpeciesId FROM InbredSet WHERE InbredSetId=%s",
+                (inbredset_id,))
+            return cursor.fetchone()[0]
+    try:
+        with (require_oauth.acquire("profile resource") as the_token,
+              database_connection(current_app.config["SQL_URI"]) as conn):
+            result = requests.get(
+                urljoin(current_app.config["AUTH_SERVER_URL"],
+                        "auth/resource/inbredset/resource-id"
+                        f"/{__species_id__(conn)}/{inbredset_id}"))
+            if result.status_code == 200:
+                resource_id = result.json()["resource-id"]
+                auth = requests.post(
+                    urljoin(current_app.config["AUTH_SERVER_URL"],
+                            "auth/resource/authorisation"),
+                    json={"resource-ids": [resource_id]},
+                    headers={"Authorization": f"Bearer {the_token.access_token}"})
+                if auth.status_code == 200:
+                    privs = (priv.privilege_id for role in auth.json()[resource_id]["roles"]
+                             for priv in role)
+                    authorisedp = all(lvl in privs for lvl in access_levels)
+                    if authorisedp:
+                        return authorisedp
+    except _HTTPException as httpe:
+        raise AuthorisationError("You need to be logged in.") from httpe
+
+    raise AuthorisationError(
+        f"User does not have the privileges {access_levels}")
+
 def __inbredset_group__(conn, inbredset_id):
     """Return InbredSet group's top-level details."""
     with conn.cursor(cursorclass=DictCursor) as cursor:
@@ -187,7 +226,7 @@ def __queue_diff__(conn: Connection, diff_data, diff_data_dir: Path) -> Path:
     """
     diff = diff_data["diff"]
     if bool(diff["Additions"]) or bool(diff["Modifications"]) or bool(diff["Deletions"]):
-        # TODO: Check user has "edit case attribute privileges"
+        __pk__(diff, "THE DIFF")
         diff_data_dir.mkdir(parents=True, exist_ok=True)
 
         created = datetime.now()
@@ -201,39 +240,45 @@ def __queue_diff__(conn: Connection, diff_data, diff_data_dir: Path) -> Path:
         return filepath
     raise NoDiffError
 
-def __apply_diff__(conn: Connection, user: User, diff_filename) -> None:
+def __apply_diff__(
+        conn: Connection, inbredset_id: int, user: User, diff_filename) -> None:
     """
     Apply the changes in the diff at `diff_filename` to the data in the database
     if the user has appropriate privileges.
     """
-    # TODO: Check user has "approve/reject case attribute diff privileges"
+    required_access(
+        inbredset_id, ("system:inbredset:edit-case-attribute",
+                       "system:inbredset:apply-case-attribute-edit"))
     def __save_diff__(conn: Connection, diff_filename):
         """Save to the database."""
         raise NotImplementedError
     raise NotImplementedError
 
-def __reject_diff__(conn: Connection, user: User, diff_filename) -> None:
+def __reject_diff__(
+        conn: Connection, inbredset_id: int, user: User, diff_filename) -> None:
     """
     Reject the changes in the diff at `diff_filename` to the data in the
     database if the user has appropriate privileges.
     """
-    # TODO: Check user has "approve/reject case attribute diff privileges"
+    required_access(
+        inbredset_id, ("system:inbredset:edit-case-attribute",
+                       "system:inbredset:apply-case-attribute-edit"))
     raise NotImplementedError
 
 @caseattr.route("/<int:inbredset_id>/add", methods=["POST"])
 def add_case_attributes(inbredset_id: int) -> Response:
     """Add a new case attribute for `InbredSetId`."""
+    required_access(inbredset_id, ("system:inbredset:create-case-attribute",))
     with (require_oauth.acquire("profile resource") as the_token,
           database_connection(current_app.config["SQL_URI"]) as conn):
-        # TODO: Check user has "add/delete case attribute privileges."
         raise NotImplementedError
 
 @caseattr.route("/<int:inbredset_id>/delete", methods=["POST"])
 def delete_case_attributes(inbredset_id: int) -> Response:
     """Delete a case attribute from `InbredSetId`."""
+    required_access(inbredset_id, ("system:inbredset:delete-case-attribute",))
     with (require_oauth.acquire("profile resource") as the_token,
           database_connection(current_app.config["SQL_URI"]) as conn):
-        # TODO: Check user has "add/delete case attribute privileges."
         raise NotImplementedError
 
 @caseattr.route("/<int:inbredset_id>/edit", methods=["POST"])
@@ -241,8 +286,10 @@ def edit_case_attributes(inbredset_id: int) -> Response:
     """Edit the case attributes for `InbredSetId` based on data received."""
     with (require_oauth.acquire("profile resource") as the_token,
           database_connection(current_app.config["SQL_URI"]) as conn):
-        # TODO: Check user has "edit case attribute privileges"
+        required_access(inbredset_id,
+                        ("system:inbredset:edit-case-attribute",))
         user = the_token.user
+        from gn3.auth.authorisation.users import DUMMY_USER; user=DUMMY_USER
         fieldnames = (["Strain"] + sorted(
             attr["Name"] for attr in
             __case_attribute_labels_by_inbred_set__(conn, inbredset_id)))
@@ -288,17 +335,15 @@ def edit_case_attributes(inbredset_id: int) -> Response:
 @caseattr.route("/approve/<path:filename>", methods=["POST"])
 def approve_case_attributes_diff(inbredset_id: int) -> Response:
     """Approve the changes to the case attributes in the diff."""
-    # TODO: Check user has "approve/reject case attribute diff privileges"
     with (require_oauth.acquire("profile resource") as the_token,
           database_connection(current_app.config["SQL_URI"]) as conn):
-        __apply_diff__(conn, the_token.user, diff_filename)
+        __apply_diff__(conn, inbredset_id, the_token.user, diff_filename)
         raise NotImplementedError
 
 @caseattr.route("/reject/<path:filename>", methods=["POST"])
 def reject_case_attributes_diff(inbredset_id: int) -> Response:
     """Reject the changes to the case attributes in the diff."""
-    # TODO: Check user has "approve/reject case attribute diff privileges"
     with (require_oauth.acquire("profile resource") as the_token,
           database_connection(current_app.config["SQL_URI"]) as conn):
-        __reject_diff__(conn, the_token.user, diff_filename)
+        __reject_diff__(conn, inbredset_id, the_token.user, diff_filename)
         raise NotImplementedError
diff --git a/gn3/settings.py b/gn3/settings.py
index 03a64c0..1eaaf15 100644
--- a/gn3/settings.py
+++ b/gn3/settings.py
@@ -75,6 +75,7 @@ ROUND_TO = 10
 
 MULTIPROCESSOR_PROCS = 6 # Number of processes to spawn
 
+AUTH_SERVER_URL=""
 AUTH_MIGRATIONS = "migrations/auth"
 AUTH_DB = os.environ.get(
     "AUTH_DB", f"{os.environ.get('HOME')}/genenetwork/gn3_files/db/auth.db")