From 3e2198e39bc229553d118f367fbd2f9932a9a76b Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 24 Apr 2023 11:45:45 +0300 Subject: auth: Return the actual privileges for the user Previously, the `oauth2/data/authorisation` endpoint was returning hard-coded values for the privileges assigned to the user for each resource. In this change, we rework to return the actual privileges for the user. --- gn3/auth/authorisation/data/views.py | 38 ++++++++++++++++++------ gn3/auth/authorisation/resources/models.py | 21 +++++-------- gn3/auth/authorisation/users/models.py | 47 ++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 22 deletions(-) (limited to 'gn3/auth') diff --git a/gn3/auth/authorisation/data/views.py b/gn3/auth/authorisation/data/views.py index 81b3e2f..d2bdbf3 100644 --- a/gn3/auth/authorisation/data/views.py +++ b/gn3/auth/authorisation/data/views.py @@ -22,6 +22,8 @@ from gn3.auth.authorisation.errors import InvalidData, NotFoundError from gn3.auth.authorisation.groups.models import group_by_id +from gn3.auth.authorisation.users.models import user_resource_roles + from gn3.auth.authorisation.resources.checks import authorised_for from gn3.auth.authorisation.resources.models import ( user_resources, public_resources, attach_resources_data) @@ -53,14 +55,17 @@ def authorisation() -> Response: with require_oauth.acquire("profile group resource") as the_token: resources = attach_resources_data( auth_conn, user_resources(auth_conn, the_token.user)) + resources_roles = user_resource_roles(auth_conn, the_token.user) privileges = { - resource_id: ("group:resource:view-resource",) + resource_id: tuple( + privilege.privilege_id + for roles in resources_roles[resource_id] + for privilege in roles.privileges)#("group:resource:view-resource",) for resource_id, is_authorised in authorised_for( auth_conn, the_token.user, ("group:resource:view-resource",), tuple( - resource.resource_id for resource in resources - if not resource.public)).items() + resource.resource_id for resource in resources)).items() if is_authorised } except _HTTPException as exc: @@ -76,18 +81,26 @@ def authorisation() -> Response: # -H "Content-Type: application/json" \ # -d '{"traits": ["HC_M2_0606_P::1442370_at", "BXDGeno::01.001.695", # "BXDPublish::10001"]}' + def __gen_key__(resource, data_item): + if resource.resource_category.resource_category_key.lower() == "phenotype": + return ( + f"{resource.resource_category.resource_category_key.lower()}::" + f"{data_item['dataset_name']}::{data_item['PublishXRefId']}") + return ( + f"{resource.resource_category.resource_category_key.lower()}::" + f"{data_item['dataset_name']}") + data_to_resource_map = { - (f"{data_item['dataset_type'].lower()}::" - f"{data_item['dataset_name']}"): resource.resource_id + __gen_key__(resource, data_item): resource.resource_id for resource in resources for data_item in resource.resource_data } privileges = { - **privileges, **{ resource.resource_id: ("system:resource:public-read",) for resource in resources if resource.public - }} + }, + **privileges} args = request.get_json() traits_names = args["traits"] # type: ignore[index] @@ -97,6 +110,14 @@ def authorisation() -> Response: "Geno": "Genotype", "Publish": "Phenotype" }[val] + + def __trait_key__(trait): + dataset_type = __translate__(trait['db']['dataset_type']).lower() + dataset_name = trait["db"]["dataset_name"] + if dataset_type == "phenotype": + return f"{dataset_type}::{dataset_name}::{trait['trait_name']}" + return f"{dataset_type}::{dataset_name}" + return jsonify(tuple( { **{key:trait[key] for key in ("trait_fullname", "trait_name")}, @@ -104,8 +125,7 @@ def authorisation() -> Response: "dataset_type": __translate__(trait["db"]["dataset_type"]), "privileges": privileges.get( data_to_resource_map.get( - f"{__translate__(trait['db']['dataset_type']).lower()}" - f"::{trait['db']['dataset_name']}", + __trait_key__(trait), uuid.UUID("4afa415e-94cb-4189-b2c6-f9ce2b6a878d")), tuple()) } for trait in diff --git a/gn3/auth/authorisation/resources/models.py b/gn3/auth/authorisation/resources/models.py index bc88691..e54ec48 100644 --- a/gn3/auth/authorisation/resources/models.py +++ b/gn3/auth/authorisation/resources/models.py @@ -423,9 +423,8 @@ def attach_mrna_resources_data( """Attach linked data to mRNA Assay resources""" placeholders = ", ".join(["?"] * len(resources)) cursor.execute( - "SELECT * FROM mrna_resources AS mr INNER JOIN linked_group_data AS lgd" - " ON (mr.dataset_id=lgd.dataset_or_trait_id " - "AND mr.dataset_type=lgd.dataset_type) " + "SELECT * FROM mrna_resources AS mr INNER JOIN linked_mrna_data AS lmd" + " ON mr.data_link_id=lmd.data_link_id " f"WHERE mr.resource_id IN ({placeholders})", tuple(str(resource.resource_id) for resource in resources)) return __attach_data__(cursor.fetchall(), resources) @@ -436,10 +435,9 @@ def attach_genotype_resources_data( placeholders = ", ".join(["?"] * len(resources)) cursor.execute( "SELECT * FROM genotype_resources AS gr " - "INNER JOIN linked_group_data AS lgd " - "ON (gr.trait_id=lgd.dataset_or_trait_id " - "AND gr.dataset_type=lgd.dataset_type) " - f"WHERE gr.resource_id IN {placeholders}", + "INNER JOIN linked_genotype_data AS lgd " + "ON gr.data_link_id=lgd.data_link_id " + f"WHERE gr.resource_id IN ({placeholders})", tuple(str(resource.resource_id) for resource in resources)) return __attach_data__(cursor.fetchall(), resources) @@ -449,10 +447,9 @@ def attach_phenotype_resources_data( placeholders = ", ".join(["?"] * len(resources)) cursor.execute( "SELECT * FROM phenotype_resources AS pr " - "INNER JOIN linked_group_data AS lgd " - "ON (pr.trait_id=lgd.dataset_or_trait_id " - "AND pr.dataset_type=lgd.dataset_type) " - f"WHERE pr.resource_id IN {placeholders}", + "INNER JOIN linked_phenotype_data AS lpd " + "ON pr.data_link_id=lpd.data_link_id " + f"WHERE pr.resource_id IN ({placeholders})", tuple(str(resource.resource_id) for resource in resources)) return __attach_data__(cursor.fetchall(), resources) @@ -532,8 +529,6 @@ def save_resource( conn, user, ("group:resource:edit-resource",), (resource_id,)) if authorised[resource_id]: with db.cursor(conn) as cursor: - params = {**dictify(resource), "public": 1 if resource.public else 0} - print(f"THE PARAMS: {params}") cursor.execute( "UPDATE resources SET " "resource_name=:resource_name, " diff --git a/gn3/auth/authorisation/users/models.py b/gn3/auth/authorisation/users/models.py index 844a8a9..8cfc1af 100644 --- a/gn3/auth/authorisation/users/models.py +++ b/gn3/auth/authorisation/users/models.py @@ -1,8 +1,11 @@ """Functions for acting on users.""" import uuid +from functools import reduce from gn3.auth import db +from gn3.auth.authorisation.roles.models import Role from gn3.auth.authorisation.checks import authorised_p +from gn3.auth.authorisation.privileges import Privilege from gn3.auth.authentication.users import User @@ -17,3 +20,47 @@ def list_users(conn: db.DbConnection) -> tuple[User, ...]: return tuple( User(uuid.UUID(row["user_id"]), row["email"], row["name"]) for row in cursor.fetchall()) + +def __build_resource_roles__(rows): + def __build_roles__(roles, row): + role_id = uuid.UUID(row["role_id"]) + priv = Privilege(row["privilege_id"], row["privilege_description"]) + role = roles.get(role_id, Role( + role_id, row["role_name"], tuple())) + return { + **roles, + role_id: Role(role_id, role.role_name, role.privileges + (priv,)) + } + def __build__(acc, row): + resource_id = uuid.UUID(row["resource_id"]) + return { + **acc, + resource_id: __build_roles__(acc.get(resource_id, {}), row) + } + return { + resource_id: tuple(roles.values()) + for resource_id, roles in reduce(__build__, rows, {}).items() + } + +# @authorised_p( +# ("",), +# ("You do not have the appropriate privileges to view a user's roles on " +# "resources.")) +def user_resource_roles(conn: db.DbConnection, user: User) -> dict[uuid.UUID, tuple[Role, ...]]: + """Fetch all the user's roles on resources.""" + with db.cursor(conn) as cursor: + cursor.execute( + "SELECT res.*, rls.*, p.*" + "FROM resources AS res INNER JOIN " + "group_user_roles_on_resources AS guror " + "ON res.resource_id=guror.resource_id " + "LEFT JOIN roles AS rls " + "ON guror.role_id=rls.role_id " + "LEFT JOIN role_privileges AS rp " + "ON rls.role_id=rp.role_id " + "LEFT JOIN privileges AS p " + "ON rp.privilege_id=p.privilege_id " + "WHERE guror.user_id = ?", + (str(user.user_id),)) + return __build_resource_roles__( + (dict(row) for row in cursor.fetchall())) -- cgit v1.2.3