about summary refs log tree commit diff
path: root/gn_auth/auth/authorisation/resources/views.py
diff options
context:
space:
mode:
Diffstat (limited to 'gn_auth/auth/authorisation/resources/views.py')
-rw-r--r--gn_auth/auth/authorisation/resources/views.py107
1 files changed, 95 insertions, 12 deletions
diff --git a/gn_auth/auth/authorisation/resources/views.py b/gn_auth/auth/authorisation/resources/views.py
index 2eda72b..a960ca3 100644
--- a/gn_auth/auth/authorisation/resources/views.py
+++ b/gn_auth/auth/authorisation/resources/views.py
@@ -18,6 +18,7 @@ from gn_auth.auth.requests import request_json
 
 from gn_auth.auth.db import sqlite3 as db
 from gn_auth.auth.db.sqlite3 import with_db_connection
+from gn_auth.auth.jwks import newest_jwk, jwks_directory
 
 from gn_auth.auth.authorisation.roles import Role
 from gn_auth.auth.authorisation.roles.models import (
@@ -38,16 +39,23 @@ from gn_auth.auth.authorisation.roles.models import (
 from gn_auth.auth.authentication.oauth2.resource_server import require_oauth
 from gn_auth.auth.authentication.users import User, user_by_id, user_by_email
 
-from .checks import authorised_for
+from .inbredset.views import popbp
+from .genotypes.views import genobp
+from .phenotypes.views import phenobp
+from .errors import MissingGroupError
+from .groups.models import Group, user_group
+from .checks import authorised_for, authorised_for_spec
 from .models import (
     Resource, resource_data, resource_by_id, public_resources,
     resource_categories, assign_resource_user, link_data_to_resource,
     unassign_resource_user, resource_category_by_id, user_roles_on_resources,
     unlink_data_from_resource, create_resource as _create_resource,
-    get_resource_id)
-from .groups.models import Group, resource_owner, group_role_by_id
+    get_resource_id, delete_resource as _delete_resource)
 
 resources = Blueprint("resources", __name__)
+resources.register_blueprint(popbp, url_prefix="/")
+resources.register_blueprint(genobp, url_prefix="/")
+resources.register_blueprint(phenobp, url_prefix="/")
 
 @resources.route("/categories", methods=["GET"])
 @require_oauth("profile group resource")
@@ -69,11 +77,17 @@ def create_resource() -> Response:
         db_uri = app.config["AUTH_DB"]
         with db.connection(db_uri) as conn:
             try:
+                group = user_group(conn, the_token.user).maybe(
+                    False, lambda grp: grp)# type: ignore[misc, arg-type]
+                if not group:
+                    raise MissingGroupError(# Not all resources require an owner group
+                        "User with no group cannot create a resource.")
                 resource = _create_resource(
                     conn,
                     resource_name,
                     resource_category_by_id(conn, resource_category_id),
                     the_token.user,
+                    group,
                     (form.get("public") == "on"))
                 return jsonify(asdict(resource))
             except sqlite3.IntegrityError as sql3ie:
@@ -85,7 +99,9 @@ def create_resource() -> Response:
                     f"{type(sql3ie)=}: {sql3ie=}")
                 raise
 
+
 @resources.route("/view/<uuid:resource_id>")
+@resources.route("/<uuid:resource_id>/view")
 @require_oauth("profile group resource")
 def view_resource(resource_id: UUID) -> Response:
     """View a particular resource's details."""
@@ -122,7 +138,7 @@ def view_resource_data(resource_id: UUID) -> Response:
     with require_oauth.acquire("profile group resource") as the_token:
         db_uri = app.config["AUTH_DB"]
         count_per_page = __safe_get_requests_count__("count_per_page")
-        offset = (__safe_get_requests_page__("page") - 1)
+        offset = __safe_get_requests_page__("page") - 1
         with db.connection(db_uri) as conn:
             resource = resource_by_id(conn, the_token.user, resource_id)
             return jsonify(resource_data(
@@ -138,7 +154,7 @@ def link_data():
     try:
         form = request_json()
         assert "resource_id" in form, "Resource ID not provided."
-        assert "data_link_id" in form, "Data Link ID not provided."
+        assert "data_link_ids" in form, "Data Link IDs not provided."
         assert "dataset_type" in form, "Dataset type not specified"
         assert form["dataset_type"].lower() in (
             "mrna", "genotype", "phenotype"), "Invalid dataset type provided."
@@ -146,8 +162,11 @@ def link_data():
         with require_oauth.acquire("profile group resource") as the_token:
             def __link__(conn: db.DbConnection):
                 return link_data_to_resource(
-                    conn, the_token.user, UUID(form["resource_id"]),
-                    form["dataset_type"], UUID(form["data_link_id"]))
+                    conn,
+                    the_token.user,
+                    UUID(form["resource_id"]),
+                    form["dataset_type"],
+                    tuple(UUID(dlinkid) for dlinkid in form["data_link_ids"]))
 
             return jsonify(with_db_connection(__link__))
     except AssertionError as aserr:
@@ -265,7 +284,7 @@ def assign_role_to_user(resource_id: UUID) -> Response:
                 user = user_by_email(conn, user_email)
                 return assign_resource_user(
                     conn, resource, user,
-                    role_by_id(conn, UUID(role_id)))
+                    role_by_id(conn, UUID(role_id)))# type: ignore[arg-type]
         except AssertionError as aserr:
             raise AuthorisationError(aserr.args[0]) from aserr
 
@@ -292,7 +311,7 @@ def unassign_role_to_user(resource_id: UUID) -> Response:
                 resource = resource_by_id(conn, _token.user, resource_id)
                 return unassign_resource_user(
                     conn, resource, user_by_id(conn, UUID(user_id)),
-                    role_by_id(conn, UUID(role_id)))
+                    role_by_id(conn, UUID(role_id)))# type: ignore[arg-type]
         except AssertionError as aserr:
             raise AuthorisationError(aserr.args[0]) from aserr
 
@@ -396,9 +415,18 @@ def resource_roles(resource_id: UUID) -> Response:
                     "ON rp.privilege_id=p.privilege_id "
                     "WHERE rr.resource_id=? AND rr.role_created_by=?",
                     (str(resource_id), str(_token.user.user_id)))
-                results = cursor.fetchall()
+                user_created = db_rows_to_roles(cursor.fetchall())
 
-            return db_rows_to_roles(results)
+                cursor.execute(
+                    "SELECT ur.user_id, ur.resource_id, r.*, p.* FROM user_roles AS ur "
+                    "INNER JOIN roles AS r ON ur.role_id=r.role_id "
+                    "INNER JOIN role_privileges AS rp ON r.role_id=rp.role_id "
+                    "INNER JOIN privileges AS p ON rp.privilege_id=p.privilege_id "
+                    "WHERE resource_id=? AND user_id=?",
+                    (str(resource_id), str(_token.user.user_id)))
+                assigned_to_user = db_rows_to_roles(cursor.fetchall())
+
+            return assigned_to_user + user_created
 
         return jsonify(with_db_connection(__roles__))
 
@@ -439,6 +467,14 @@ def resources_authorisation():
                 "Expected a JSON object with a 'resource-ids' key.")
         })
         resp.status_code = 400
+    except Exception as _exc:#pylint: disable=[broad-except]
+        app.logger.debug("Generic exception.", exc_info=True)
+        resp = jsonify({
+            "status": "general-exception",
+            "error_description": (
+                "Failed to fetch the user's privileges.")
+        })
+        resp.status_code = 500
 
     return resp
 
@@ -491,7 +527,8 @@ def get_user_roles_on_resource(name) -> Response:
             "email": _token.user.email,
             "roles": roles,
         }
-        token = jwt.encode(jose_header, payload, app.config["SSL_PRIVATE_KEY"])
+        token = jwt.encode(
+            jose_header, payload, newest_jwk(jwks_directory(app)))
         response.headers["Authorization"] = f"Bearer {token.decode('utf-8')}"
         return response
 
@@ -637,3 +674,49 @@ def user_resource_roles(resource_id: UUID, user_id: UUID):
 
         return jsonify([asdict(role) for role in
                         _user_resource_roles(conn, _token.user, _resource)])
+
+
+@resources.route("/delete", methods=["POST"])
+@require_oauth("profile group resource")
+def delete_resource():
+    """Delete the specified resource, if possible."""
+    with (require_oauth.acquire("profile group resource") as the_token,
+          db.connection(app.config["AUTH_DB"]) as conn):
+        form = request_json()
+        try:
+            resource_id = UUID(form.get("resource_id"))
+            if not authorised_for_spec(
+                    conn,
+                    the_token.user.user_id,
+                    resource_id,
+                    "(OR group:resource:delete-resource system:resource:delete)"):
+                raise AuthorisationError("You do not have the appropriate "
+                                         "privileges to delete this resource.")
+
+            data = resource_data(
+                conn,
+                resource_by_id(conn, the_token.user, resource_id),
+                0,
+                10)
+            if bool(data):
+                return jsonify({
+                    "error": "NonEmptyResouce",
+                    "error-description": "Cannot delete a resource with linked data"
+                }), 400
+
+            _delete_resource(conn, resource_id)
+            return jsonify({
+                "description": f"Successfully deleted resource with ID '{resource_id}'."
+            })
+        except ValueError as _verr:
+            app.logger.debug("Error!", exc_info=True)
+            return jsonify({
+                "error": "ValueError",
+                "error-description": "An invalid identifier was provided"
+            }), 400
+        except TypeError as _terr:
+            app.logger.debug("Error!", exc_info=True)
+            return jsonify({
+                "error": "TypeError",
+                "error-description": "An invalid identifier was provided"
+            }), 400