diff options
Diffstat (limited to 'gn_auth/auth/authorisation/resources/models.py')
-rw-r--r-- | gn_auth/auth/authorisation/resources/models.py | 172 |
1 files changed, 72 insertions, 100 deletions
diff --git a/gn_auth/auth/authorisation/resources/models.py b/gn_auth/auth/authorisation/resources/models.py index 95a7f1c..e538a87 100644 --- a/gn_auth/auth/authorisation/resources/models.py +++ b/gn_auth/auth/authorisation/resources/models.py @@ -4,6 +4,8 @@ from uuid import UUID, uuid4 from functools import reduce, partial from typing import Dict, Sequence, Optional +import sqlite3 + from gn_auth.auth.db import sqlite3 as db from gn_auth.auth.authentication.users import User from gn_auth.auth.db.sqlite3 import with_db_connection @@ -14,79 +16,59 @@ from gn_auth.auth.authorisation.checks import authorised_p from gn_auth.auth.errors import NotFoundError, AuthorisationError from .checks import authorised_for -from .base import Resource, ResourceCategory -from .groups.models import Group, GroupRole, user_group, is_group_leader +from .base import Resource, ResourceCategory, resource_from_dbrow +from .common import assign_resource_owner_role +from .groups.models import Group, is_group_leader from .mrna import ( resource_data as mrna_resource_data, attach_resources_data as mrna_attach_resources_data, link_data_to_resource as mrna_link_data_to_resource, unlink_data_from_resource as mrna_unlink_data_from_resource) -from .genotype import ( +from .genotypes.models import ( resource_data as genotype_resource_data, attach_resources_data as genotype_attach_resources_data, link_data_to_resource as genotype_link_data_to_resource, unlink_data_from_resource as genotype_unlink_data_from_resource) -from .phenotype import ( +from .phenotypes.models import ( resource_data as phenotype_resource_data, attach_resources_data as phenotype_attach_resources_data, link_data_to_resource as phenotype_link_data_to_resource, unlink_data_from_resource as phenotype_unlink_data_from_resource) -from .errors import MissingGroupError - -def __assign_resource_owner_role__(cursor, resource, user): - """Assign `user` the 'Resource Owner' role for `resource`.""" - cursor.execute( - "SELECT rr.* FROM resource_roles AS rr INNER JOIN roles AS r " - "ON rr.role_id=r.role_id WHERE r.role_name='resource-owner' " - "AND rr.resource_id=?", - (str(resource.resource_id),)) - role = cursor.fetchone() - if not role: - cursor.execute("SELECT * FROM roles WHERE role_name='resource-owner'") - role = cursor.fetchone() - cursor.execute( - "INSERT INTO resource_roles(resource_id, role_created_by, role_id) " - "VALUES (:resource_id, :user_id, :role_id)", - {"resource_id": str(resource.resource_id), - "user_id": str(user.user_id), - "role_id": role["role_id"]}) - - cursor.execute( - "INSERT INTO user_roles " - "VALUES (:user_id, :role_id, :resource_id) " - "ON CONFLICT (user_id, role_id, resource_id) DO NOTHING", - { - "user_id": str(user.user_id), - "role_id": role["role_id"], - "resource_id": str(resource.resource_id) - }) @authorised_p(("group:resource:create-resource",), error_description="Insufficient privileges to create a resource", oauth2_scope="profile resource") -def create_resource( - conn: db.DbConnection, resource_name: str, - resource_category: ResourceCategory, user: User, - public: bool) -> Resource: +def create_resource(# pylint: disable=[too-many-arguments, too-many-positional-arguments] + cursor: sqlite3.Cursor, + resource_name: str, + resource_category: ResourceCategory, + user: User, + group: Group, + public: bool +) -> Resource: """Create a resource item.""" - with db.cursor(conn) as cursor: - group = user_group(conn, 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 = Resource(uuid4(), resource_name, resource_category, public) - cursor.execute( - "INSERT INTO resources VALUES (?, ?, ?, ?)", - (str(resource.resource_id), - resource_name, - str(resource.resource_category.resource_category_id), - 1 if resource.public else 0)) - cursor.execute("INSERT INTO resource_ownership (group_id, resource_id) " - "VALUES (?, ?)", - (str(group.group_id), str(resource.resource_id))) - __assign_resource_owner_role__(cursor, resource, user) + resource = Resource(uuid4(), resource_name, resource_category, public) + cursor.execute( + "INSERT INTO resources VALUES (?, ?, ?, ?)", + (str(resource.resource_id), + resource_name, + str(resource.resource_category.resource_category_id), + 1 if resource.public else 0)) + # TODO: @fredmanglis,@rookie101 + # 1. Move the actions below into a (the?) hooks system + # 2. Do more checks: A resource can have varying hooks depending on type + # e.g. if mRNA, pheno or geno resource, assign: + # - "resource-owner" + # if inbredset-group, assign: + # - "resource-owner", + # - "inbredset-group-owner" etc. + # if resource is of type "group", assign: + # - group-leader + cursor.execute("INSERT INTO resource_ownership (group_id, resource_id) " + "VALUES (?, ?)", + (str(group.group_id), str(resource.resource_id))) + assign_resource_owner_role(cursor, resource.resource_id, user.user_id) return resource @@ -149,32 +131,21 @@ def group_leader_resources( def user_resources(conn: db.DbConnection, user: User) -> Sequence[Resource]: """List the resources available to the user""" - categories = { # Repeated in `public_resources` function - cat.resource_category_id: cat for cat in resource_categories(conn) - } with db.cursor(conn) as cursor: - def __all_resources__(group) -> Sequence[Resource]: - gl_resources = group_leader_resources(conn, user, group, categories) + cursor.execute( + ("SELECT DISTINCT(r.resource_id), r.resource_name, " + "r.resource_category_id, r.public, rc.resource_category_key, " + "rc.resource_category_description " + "FROM user_roles AS ur " + "INNER JOIN resources AS r ON ur.resource_id=r.resource_id " + "INNER JOIN resource_categories AS rc " + "ON r.resource_category_id=rc.resource_category_id " + "WHERE ur.user_id=?"), + (str(user.user_id),)) + rows = cursor.fetchall() or [] + + return tuple(resource_from_dbrow(row) for row in rows) - cursor.execute( - ("SELECT resources.* FROM user_roles LEFT JOIN resources " - "ON user_roles.resource_id=resources.resource_id " - "WHERE user_roles.user_id=?"), - (str(user.user_id),)) - rows = cursor.fetchall() - private_res = tuple( - Resource(UUID(row[0]), row[1], categories[UUID(row[2])], - bool(row[3])) - for row in rows) - return tuple({ - res.resource_id: res - for res in - (private_res + gl_resources + public_resources(conn))# type: ignore[operator] - }.values()) - - # Fix the typing here - return user_group(conn, user).map(__all_resources__).maybe(# type: ignore[arg-type,misc] - public_resources(conn), lambda res: res)# type: ignore[arg-type,return-value] def resource_data(conn, resource, offset: int = 0, limit: Optional[int] = None) -> tuple[dict, ...]: """ @@ -236,8 +207,12 @@ def resource_by_id( raise NotFoundError(f"Could not find a resource with id '{resource_id}'") def link_data_to_resource( - conn: db.DbConnection, user: User, resource_id: UUID, dataset_type: str, - data_link_id: UUID) -> dict: + conn: db.DbConnection, + user: User, + resource_id: UUID, + dataset_type: str, + data_link_ids: tuple[UUID, ...] +) -> tuple[dict, ...]: """Link data to resource.""" if not authorised_for( conn, user, ("group:resource:edit-resource",), @@ -252,7 +227,7 @@ def link_data_to_resource( "mrna": mrna_link_data_to_resource, "genotype": genotype_link_data_to_resource, "phenotype": phenotype_link_data_to_resource, - }[dataset_type.lower()](conn, resource, data_link_id) + }[dataset_type.lower()](conn, resource, data_link_ids) def unlink_data_from_resource( conn: db.DbConnection, user: User, resource_id: UUID, data_link_id: UUID): @@ -305,13 +280,13 @@ def attach_resources_data( for category, rscs in organised.items()) for resource in categories) -@authorised_p( - ("group:user:assign-role",), - "You cannot assign roles to users for this group.", - oauth2_scope="profile group role resource") + def assign_resource_user( - conn: db.DbConnection, resource: Resource, user: User, - role: GroupRole) -> dict: + conn: db.DbConnection, + resource: Resource, + user: User, + role: Role +) -> dict: """Assign `role` to `user` for the specific `resource`.""" with db.cursor(conn) as cursor: cursor.execute( @@ -319,39 +294,36 @@ def assign_resource_user( "VALUES (?, ?, ?) " "ON CONFLICT (user_id, role_id, resource_id) " "DO NOTHING", - (str(user.user_id), str(role.role.role_id), - str(resource.resource_id))) + (str(user.user_id), str(role.role_id), str(resource.resource_id))) return { "resource": asdict(resource), "user": asdict(user), "role": asdict(role), "description": ( f"The user '{user.name}'({user.email}) was assigned the " - f"'{role.role.role_name}' role on resource with ID " + f"'{role.role_name}' role on resource with ID " f"'{resource.resource_id}'.")} -@authorised_p( - ("group:user:assign-role",), - "You cannot assign roles to users for this group.", - oauth2_scope="profile group role resource") + def unassign_resource_user( - conn: db.DbConnection, resource: Resource, user: User, - role: GroupRole) -> dict: + conn: db.DbConnection, + resource: Resource, + user: User, + role: Role +) -> dict: """Assign `role` to `user` for the specific `resource`.""" with db.cursor(conn) as cursor: cursor.execute( "DELETE FROM user_roles " "WHERE user_id=? AND role_id=? AND resource_id=?", - (str(user.user_id), - str(role.role.role_id), - str(resource.resource_id))) + (str(user.user_id), str(role.role_id), str(resource.resource_id))) return { "resource": asdict(resource), "user": asdict(user), "role": asdict(role), "description": ( f"The user '{user.name}'({user.email}) had the " - f"'{role.role.role_name}' role on resource with ID " + f"'{role.role_name}' role on resource with ID " f"'{resource.resource_id}' taken away.")} def save_resource( |