about summary refs log tree commit diff
path: root/gn3/auth/authorisation/resources
diff options
context:
space:
mode:
Diffstat (limited to 'gn3/auth/authorisation/resources')
-rw-r--r--gn3/auth/authorisation/resources/__init__.py2
-rw-r--r--gn3/auth/authorisation/resources/checks.py47
-rw-r--r--gn3/auth/authorisation/resources/models.py579
-rw-r--r--gn3/auth/authorisation/resources/views.py272
4 files changed, 0 insertions, 900 deletions
diff --git a/gn3/auth/authorisation/resources/__init__.py b/gn3/auth/authorisation/resources/__init__.py
deleted file mode 100644
index 869ab60..0000000
--- a/gn3/auth/authorisation/resources/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-"""Initialise the `gn3.auth.authorisation.resources` package."""
-from .models import Resource, ResourceCategory
diff --git a/gn3/auth/authorisation/resources/checks.py b/gn3/auth/authorisation/resources/checks.py
deleted file mode 100644
index 1f5a0f9..0000000
--- a/gn3/auth/authorisation/resources/checks.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""Handle authorisation checks for resources"""
-from uuid import UUID
-from functools import reduce
-from typing import Sequence
-
-from gn3.auth import db
-from gn3.auth.authorisation.users import User
-
-def __organise_privileges_by_resource_id__(rows):
-    def __organise__(privs, row):
-        resource_id = UUID(row["resource_id"])
-        return {
-            **privs,
-            resource_id: (row["privilege_id"],) + privs.get(
-                resource_id, tuple())
-        }
-    return reduce(__organise__, rows, {})
-
-def authorised_for(conn: db.DbConnection, user: User, privileges: tuple[str],
-                   resource_ids: Sequence[UUID]) -> dict[UUID, bool]:
-    """
-    Check whether `user` is authorised to access `resources` according to given
-    `privileges`.
-    """
-    with db.cursor(conn) as cursor:
-        cursor.execute(
-            ("SELECT guror.*, rp.privilege_id FROM "
-             "group_user_roles_on_resources AS guror "
-             "INNER JOIN group_roles AS gr ON  "
-             "(guror.group_id=gr.group_id AND guror.role_id=gr.role_id) "
-             "INNER JOIN roles AS r ON gr.role_id=r.role_id "
-             "INNER JOIN role_privileges AS rp ON r.role_id=rp.role_id "
-             "WHERE guror.user_id=? "
-             f"AND guror.resource_id IN ({', '.join(['?']*len(resource_ids))})"
-             f"AND rp.privilege_id IN ({', '.join(['?']*len(privileges))})"),
-            ((str(user.user_id),) + tuple(
-                str(r_id) for r_id in resource_ids) + tuple(privileges)))
-        resource_privileges = __organise_privileges_by_resource_id__(
-            cursor.fetchall())
-        authorised = tuple(resource_id for resource_id, res_privileges
-                           in resource_privileges.items()
-                           if all(priv in res_privileges
-                                  for priv in privileges))
-        return {
-            resource_id: resource_id in authorised
-            for resource_id in resource_ids
-        }
diff --git a/gn3/auth/authorisation/resources/models.py b/gn3/auth/authorisation/resources/models.py
deleted file mode 100644
index cf7769e..0000000
--- a/gn3/auth/authorisation/resources/models.py
+++ /dev/null
@@ -1,579 +0,0 @@
-"""Handle the management of resources."""
-import json
-import sqlite3
-from uuid import UUID, uuid4
-from functools import reduce, partial
-from typing import Any, Dict, Sequence, Optional, NamedTuple
-
-from gn3.auth import db
-from gn3.auth.dictify import dictify
-from gn3.auth.authorisation.users import User
-from gn3.auth.db_utils import with_db_connection
-
-from .checks import authorised_for
-
-from ..checks import authorised_p
-from ..errors import NotFoundError, AuthorisationError
-from ..groups.models import (
-    Group, GroupRole, user_group, group_by_id, is_group_leader)
-
-class MissingGroupError(AuthorisationError):
-    """Raised for any resource operation without a group."""
-
-class ResourceCategory(NamedTuple):
-    """Class representing a resource category."""
-    resource_category_id: UUID
-    resource_category_key: str
-    resource_category_description: str
-
-    def dictify(self) -> dict[str, Any]:
-        """Return a dict representation of `ResourceCategory` objects."""
-        return {
-            "resource_category_id": self.resource_category_id,
-            "resource_category_key": self.resource_category_key,
-            "resource_category_description": self.resource_category_description
-        }
-
-class Resource(NamedTuple):
-    """Class representing a resource."""
-    group: Group
-    resource_id: UUID
-    resource_name: str
-    resource_category: ResourceCategory
-    public: bool
-    resource_data: Sequence[dict[str, Any]] = tuple()
-
-    def dictify(self) -> dict[str, Any]:
-        """Return a dict representation of `Resource` objects."""
-        return {
-            "group": dictify(self.group), "resource_id": self.resource_id,
-            "resource_name": self.resource_name,
-            "resource_category": dictify(self.resource_category),
-            "public": self.public,
-            "resource_data": self.resource_data
-        }
-
-def __assign_resource_owner_role__(cursor, resource, user):
-    """Assign `user` the 'Resource Owner' role for `resource`."""
-    cursor.execute(
-        "SELECT gr.* FROM group_roles AS gr INNER JOIN roles AS r "
-        "ON gr.role_id=r.role_id WHERE r.role_name='resource-owner' "
-        "AND gr.group_id=?",
-        (str(resource.group.group_id),))
-    role = cursor.fetchone()
-    if not role:
-        cursor.execute("SELECT * FROM roles WHERE role_name='resource-owner'")
-        role = cursor.fetchone()
-        cursor.execute(
-            "INSERT INTO group_roles VALUES "
-            "(:group_role_id, :group_id, :role_id)",
-            {"group_role_id": str(uuid4()),
-             "group_id": str(resource.group.group_id),
-             "role_id": role["role_id"]})
-
-    cursor.execute(
-            "INSERT INTO group_user_roles_on_resources "
-            "VALUES ("
-            ":group_id, :user_id, :role_id, :resource_id"
-            ")",
-            {"group_id": str(resource.group.group_id),
-             "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:
-    """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(
-                "User with no group cannot create a resource.")
-        resource = Resource(
-            group, uuid4(), resource_name, resource_category, public)
-        cursor.execute(
-            "INSERT INTO resources VALUES (?, ?, ?, ?, ?)",
-            (str(resource.group.group_id), str(resource.resource_id),
-             resource_name,
-             str(resource.resource_category.resource_category_id),
-             1 if resource.public else 0))
-        __assign_resource_owner_role__(cursor, resource, user)
-
-    return resource
-
-def resource_category_by_id(
-        conn: db.DbConnection, category_id: UUID) -> ResourceCategory:
-    """Retrieve a resource category by its ID."""
-    with db.cursor(conn) as cursor:
-        cursor.execute(
-            "SELECT * FROM resource_categories WHERE "
-            "resource_category_id=?",
-            (str(category_id),))
-        results = cursor.fetchone()
-        if results:
-            return ResourceCategory(
-                UUID(results["resource_category_id"]),
-                results["resource_category_key"],
-                results["resource_category_description"])
-
-    raise NotFoundError(
-        f"Could not find a ResourceCategory with ID '{category_id}'")
-
-def resource_categories(conn: db.DbConnection) -> Sequence[ResourceCategory]:
-    """Retrieve all available resource categories"""
-    with db.cursor(conn) as cursor:
-        cursor.execute("SELECT * FROM resource_categories")
-        return tuple(
-            ResourceCategory(UUID(row[0]), row[1], row[2])
-            for row in cursor.fetchall())
-    return tuple()
-
-def public_resources(conn: db.DbConnection) -> Sequence[Resource]:
-    """List all resources marked as public"""
-    categories = {
-        str(cat.resource_category_id): cat for cat in resource_categories(conn)
-    }
-    with db.cursor(conn) as cursor:
-        cursor.execute("SELECT * FROM resources WHERE public=1")
-        results = cursor.fetchall()
-        group_uuids = tuple(row[0] for row in results)
-        query = ("SELECT * FROM groups WHERE group_id IN "
-                 f"({', '.join(['?'] * len(group_uuids))})")
-        cursor.execute(query, group_uuids)
-        groups = {
-            row[0]: Group(
-                UUID(row[0]), row[1], json.loads(row[2] or "{}"))
-            for row in cursor.fetchall()
-        }
-        return tuple(
-            Resource(groups[row[0]], UUID(row[1]), row[2], categories[row[3]],
-                     bool(row[4]))
-            for row in results)
-
-def group_leader_resources(
-        conn: db.DbConnection, user: User, group: Group,
-        res_categories: Dict[UUID, ResourceCategory]) -> Sequence[Resource]:
-    """Return all the resources available to the group leader"""
-    if is_group_leader(conn, user, group):
-        with db.cursor(conn) as cursor:
-            cursor.execute("SELECT * FROM resources WHERE group_id=?",
-                           (str(group.group_id),))
-            return tuple(
-                Resource(group, UUID(row[1]), row[2],
-                         res_categories[UUID(row[3])], bool(row[4]))
-                for row in cursor.fetchall())
-    return tuple()
-
-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 resources.* FROM group_user_roles_on_resources "
-                 "LEFT JOIN resources "
-                 "ON group_user_roles_on_resources.resource_id=resources.resource_id "
-                 "WHERE group_user_roles_on_resources.group_id = ? "
-                 "AND group_user_roles_on_resources.user_id = ?"),
-                (str(group.group_id), str(user.user_id)))
-            rows = cursor.fetchall()
-            private_res = tuple(
-                Resource(group, UUID(row[1]), row[2], categories[UUID(row[3])],
-                         bool(row[4]))
-                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, ...]:
-    """
-    Retrieve the data for `resource`, optionally limiting the number of items.
-    """
-    resource_data_function = {
-        "mrna": mrna_resource_data,
-        "genotype": genotype_resource_data,
-        "phenotype": phenotype_resource_data
-    }
-    with db.cursor(conn) as cursor:
-        return tuple(
-            dict(data_row) for data_row in
-            resource_data_function[
-                resource.resource_category.resource_category_key](
-                    cursor, resource.resource_id, offset, limit))
-
-def attach_resource_data(cursor: db.DbCursor, resource: Resource) -> Resource:
-    """Attach the linked data to the resource"""
-    resource_data_function = {
-        "mrna": mrna_resource_data,
-        "genotype": genotype_resource_data,
-        "phenotype": phenotype_resource_data
-    }
-    category = resource.resource_category
-    data_rows = tuple(
-        dict(data_row) for data_row in
-        resource_data_function[category.resource_category_key](
-            cursor, resource.resource_id))
-    return Resource(
-        resource.group, resource.resource_id, resource.resource_name,
-        resource.resource_category, resource.public, data_rows)
-
-def mrna_resource_data(cursor: db.DbCursor,
-                       resource_id: UUID,
-                       offset: int = 0,
-                       limit: Optional[int] = None) -> Sequence[sqlite3.Row]:
-    """Fetch data linked to a mRNA resource"""
-    cursor.execute(
-        (("SELECT * FROM mrna_resources AS mr "
-          "INNER JOIN linked_mrna_data AS lmr "
-          "ON mr.data_link_id=lmr.data_link_id "
-          "WHERE mr.resource_id=?") + (
-              f" LIMIT {limit} OFFSET {offset}" if bool(limit) else "")),
-        (str(resource_id),))
-    return cursor.fetchall()
-
-def genotype_resource_data(
-        cursor: db.DbCursor,
-        resource_id: UUID,
-        offset: int = 0,
-        limit: Optional[int] = None) -> Sequence[sqlite3.Row]:
-    """Fetch data linked to a Genotype resource"""
-    cursor.execute(
-        (("SELECT * FROM genotype_resources AS gr "
-          "INNER JOIN linked_genotype_data AS lgd "
-          "ON gr.data_link_id=lgd.data_link_id "
-          "WHERE gr.resource_id=?") + (
-              f" LIMIT {limit} OFFSET {offset}" if bool(limit) else "")),
-        (str(resource_id),))
-    return cursor.fetchall()
-
-def phenotype_resource_data(
-        cursor: db.DbCursor,
-        resource_id: UUID,
-        offset: int = 0,
-        limit: Optional[int] = None) -> Sequence[sqlite3.Row]:
-    """Fetch data linked to a Phenotype resource"""
-    cursor.execute(
-        ("SELECT * FROM phenotype_resources AS pr "
-         "INNER JOIN linked_phenotype_data AS lpd "
-         "ON pr.data_link_id=lpd.data_link_id "
-         "WHERE pr.resource_id=?") + (
-             f" LIMIT {limit} OFFSET {offset}" if bool(limit) else ""),
-        (str(resource_id),))
-    return cursor.fetchall()
-
-def resource_by_id(
-        conn: db.DbConnection, user: User, resource_id: UUID) -> Resource:
-    """Retrieve a resource by its ID."""
-    if not authorised_for(
-            conn, user, ("group:resource:view-resource",),
-            (resource_id,))[resource_id]:
-        raise AuthorisationError(
-            "You are not authorised to access resource with id "
-            f"'{resource_id}'.")
-
-    with db.cursor(conn) as cursor:
-        cursor.execute("SELECT * FROM resources WHERE resource_id=:id",
-                       {"id": str(resource_id)})
-        row = cursor.fetchone()
-        if row:
-            return Resource(
-                group_by_id(conn, UUID(row["group_id"])),
-                UUID(row["resource_id"]), row["resource_name"],
-                resource_category_by_id(conn, row["resource_category_id"]),
-                bool(int(row["public"])))
-
-    raise NotFoundError(f"Could not find a resource with id '{resource_id}'")
-
-def __link_mrna_data_to_resource__(
-        conn: db.DbConnection, resource: Resource, data_link_id: UUID) -> dict:
-    """Link mRNA Assay data with a resource."""
-    with db.cursor(conn) as cursor:
-        params = {
-            "group_id": str(resource.group.group_id),
-            "resource_id": str(resource.resource_id),
-            "data_link_id": str(data_link_id)
-        }
-        cursor.execute(
-            "INSERT INTO mrna_resources VALUES"
-            "(:group_id, :resource_id, :data_link_id)",
-            params)
-        return params
-
-def __link_geno_data_to_resource__(
-        conn: db.DbConnection, resource: Resource, data_link_id: UUID) -> dict:
-    """Link Genotype data with a resource."""
-    with db.cursor(conn) as cursor:
-        params = {
-            "group_id": str(resource.group.group_id),
-            "resource_id": str(resource.resource_id),
-            "data_link_id": str(data_link_id)
-        }
-        cursor.execute(
-            "INSERT INTO genotype_resources VALUES"
-            "(:group_id, :resource_id, :data_link_id)",
-            params)
-        return params
-
-def __link_pheno_data_to_resource__(
-        conn: db.DbConnection, resource: Resource, data_link_id: UUID) -> dict:
-    """Link Phenotype data with a resource."""
-    with db.cursor(conn) as cursor:
-        params = {
-            "group_id": str(resource.group.group_id),
-            "resource_id": str(resource.resource_id),
-            "data_link_id": str(data_link_id)
-        }
-        cursor.execute(
-            "INSERT INTO phenotype_resources VALUES"
-            "(:group_id, :resource_id, :data_link_id)",
-            params)
-        return params
-
-def link_data_to_resource(
-        conn: db.DbConnection, user: User, resource_id: UUID, dataset_type: str,
-        data_link_id: UUID) -> dict:
-    """Link data to resource."""
-    if not authorised_for(
-            conn, user, ("group:resource:edit-resource",),
-            (resource_id,))[resource_id]:
-        raise AuthorisationError(
-            "You are not authorised to link data to resource with id "
-            f"{resource_id}")
-
-    resource = with_db_connection(partial(
-        resource_by_id, user=user, resource_id=resource_id))
-    return {
-        "mrna": __link_mrna_data_to_resource__,
-        "genotype": __link_geno_data_to_resource__,
-        "phenotype": __link_pheno_data_to_resource__,
-    }[dataset_type.lower()](conn, resource, data_link_id)
-
-def __unlink_mrna_data_to_resource__(
-        conn: db.DbConnection, resource: Resource, data_link_id: UUID) -> dict:
-    """Unlink data from mRNA Assay resources"""
-    with db.cursor(conn) as cursor:
-        cursor.execute("DELETE FROM mrna_resources "
-                       "WHERE resource_id=? AND data_link_id=?",
-                       (str(resource.resource_id), str(data_link_id)))
-        return {
-            "resource_id": str(resource.resource_id),
-            "dataset_type": resource.resource_category.resource_category_key,
-            "data_link_id": data_link_id
-        }
-
-def __unlink_geno_data_to_resource__(
-        conn: db.DbConnection, resource: Resource, data_link_id: UUID) -> dict:
-    """Unlink data from Genotype resources"""
-    with db.cursor(conn) as cursor:
-        cursor.execute("DELETE FROM genotype_resources "
-                       "WHERE resource_id=? AND data_link_id=?",
-                       (str(resource.resource_id), str(data_link_id)))
-        return {
-            "resource_id": str(resource.resource_id),
-            "dataset_type": resource.resource_category.resource_category_key,
-            "data_link_id": data_link_id
-        }
-
-def __unlink_pheno_data_to_resource__(
-        conn: db.DbConnection, resource: Resource, data_link_id: UUID) -> dict:
-    """Unlink data from Phenotype resources"""
-    with db.cursor(conn) as cursor:
-        cursor.execute("DELETE FROM phenotype_resources "
-                       "WHERE resource_id=? AND data_link_id=?",
-                       (str(resource.resource_id), str(data_link_id)))
-        return {
-            "resource_id": str(resource.resource_id),
-            "dataset_type": resource.resource_category.resource_category_key,
-            "data_link_id": str(data_link_id)
-        }
-
-def unlink_data_from_resource(
-        conn: db.DbConnection, user: User, resource_id: UUID, data_link_id: UUID):
-    """Unlink data from resource."""
-    if not authorised_for(
-            conn, user, ("group:resource:edit-resource",),
-            (resource_id,))[resource_id]:
-        raise AuthorisationError(
-            "You are not authorised to link data to resource with id "
-            f"{resource_id}")
-
-    resource = with_db_connection(partial(
-        resource_by_id, user=user, resource_id=resource_id))
-    dataset_type = resource.resource_category.resource_category_key
-    return {
-        "mrna": __unlink_mrna_data_to_resource__,
-        "genotype": __unlink_geno_data_to_resource__,
-        "phenotype": __unlink_pheno_data_to_resource__,
-    }[dataset_type.lower()](conn, resource, data_link_id)
-
-def organise_resources_by_category(resources: Sequence[Resource]) -> dict[
-        ResourceCategory, tuple[Resource]]:
-    """Organise the `resources` by their categories."""
-    def __organise__(accumulator, resource):
-        category = resource.resource_category
-        return {
-            **accumulator,
-            category: accumulator.get(category, tuple()) + (resource,)
-        }
-    return reduce(__organise__, resources, {})
-
-def __attach_data__(
-        data_rows: Sequence[sqlite3.Row],
-        resources: Sequence[Resource]) -> Sequence[Resource]:
-    def __organise__(acc, row):
-        resource_id = UUID(row["resource_id"])
-        return {
-            **acc,
-            resource_id: acc.get(resource_id, tuple()) + (dict(row),)
-        }
-    organised: dict[UUID, tuple[dict, ...]] = reduce(__organise__, data_rows, {})
-    return tuple(
-        Resource(
-            resource.group, resource.resource_id, resource.resource_name,
-            resource.resource_category, resource.public,
-            organised.get(resource.resource_id, tuple()))
-        for resource in resources)
-
-def attach_mrna_resources_data(
-        cursor, resources: Sequence[Resource]) -> Sequence[Resource]:
-    """Attach linked data to mRNA Assay resources"""
-    placeholders = ", ".join(["?"] * len(resources))
-    cursor.execute(
-        "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)
-
-def attach_genotype_resources_data(
-        cursor, resources: Sequence[Resource]) -> Sequence[Resource]:
-    """Attach linked data to Genotype resources"""
-    placeholders = ", ".join(["?"] * len(resources))
-    cursor.execute(
-        "SELECT * FROM genotype_resources AS gr "
-        "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)
-
-def attach_phenotype_resources_data(
-        cursor, resources: Sequence[Resource]) -> Sequence[Resource]:
-    """Attach linked data to Phenotype resources"""
-    placeholders = ", ".join(["?"] * len(resources))
-    cursor.execute(
-        "SELECT * FROM phenotype_resources AS pr "
-        "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)
-
-def attach_resources_data(
-        conn: db.DbConnection, resources: Sequence[Resource]) -> Sequence[
-            Resource]:
-    """Attach linked data for each resource in `resources`"""
-    resource_data_function = {
-        "mrna": attach_mrna_resources_data,
-        "genotype": attach_genotype_resources_data,
-        "phenotype": attach_phenotype_resources_data
-    }
-    organised = organise_resources_by_category(resources)
-    with db.cursor(conn) as cursor:
-        return tuple(
-            resource for categories in
-            (resource_data_function[category.resource_category_key](
-                cursor, rscs)
-             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:
-    """Assign `role` to `user` for the specific `resource`."""
-    with db.cursor(conn) as cursor:
-        cursor.execute(
-            "INSERT INTO "
-            "group_user_roles_on_resources(group_id, user_id, role_id, "
-            "resource_id) "
-            "VALUES (?, ?, ?, ?) "
-            "ON CONFLICT (group_id, user_id, role_id, resource_id) "
-            "DO NOTHING",
-            (str(resource.group.group_id), str(user.user_id),
-             str(role.role.role_id), str(resource.resource_id)))
-        return {
-            "resource": dictify(resource),
-            "user": dictify(user),
-            "role": dictify(role),
-            "description": (
-                f"The user '{user.name}'({user.email}) was assigned the "
-                f"'{role.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:
-    """Assign `role` to `user` for the specific `resource`."""
-    with db.cursor(conn) as cursor:
-        cursor.execute(
-            "DELETE FROM group_user_roles_on_resources "
-            "WHERE group_id=? AND user_id=? AND role_id=? AND resource_id=?",
-            (str(resource.group.group_id), str(user.user_id),
-             str(role.role.role_id), str(resource.resource_id)))
-        return {
-            "resource": dictify(resource),
-            "user": dictify(user),
-            "role": dictify(role),
-            "description": (
-                f"The user '{user.name}'({user.email}) had the "
-                f"'{role.role.role_name}' role on resource with ID "
-                f"'{resource.resource_id}' taken away.")}
-
-def save_resource(
-        conn: db.DbConnection, user: User, resource: Resource) -> Resource:
-    """Update an existing resource."""
-    resource_id = resource.resource_id
-    authorised = authorised_for(
-        conn, user, ("group:resource:edit-resource",), (resource_id,))
-    if authorised[resource_id]:
-        with db.cursor(conn) as cursor:
-            cursor.execute(
-                "UPDATE resources SET "
-                "resource_name=:resource_name, "
-                "public=:public "
-                "WHERE group_id=:group_id "
-                "AND resource_id=:resource_id",
-                {
-                    "resource_name": resource.resource_name,
-                    "public": 1 if resource.public else 0,
-                    "group_id": str(resource.group.group_id),
-                    "resource_id": str(resource.resource_id)
-                })
-            return resource
-
-    raise AuthorisationError(
-        "You do not have the appropriate privileges to edit this resource.")
diff --git a/gn3/auth/authorisation/resources/views.py b/gn3/auth/authorisation/resources/views.py
deleted file mode 100644
index bda67cd..0000000
--- a/gn3/auth/authorisation/resources/views.py
+++ /dev/null
@@ -1,272 +0,0 @@
-"""The views/routes for the resources package"""
-import uuid
-import json
-import sqlite3
-from functools import reduce
-
-from flask import request, jsonify, Response, Blueprint, current_app as app
-
-from gn3.auth.db_utils import with_db_connection
-from gn3.auth.authorisation.oauth2.resource_server import require_oauth
-from gn3.auth.authorisation.users import User, user_by_id, user_by_email
-
-from .checks import authorised_for
-from .models import (
-    Resource, save_resource, resource_data, resource_by_id, resource_categories,
-    assign_resource_user, link_data_to_resource, unassign_resource_user,
-    resource_category_by_id, unlink_data_from_resource,
-    create_resource as _create_resource)
-
-from ..roles import Role
-from ..errors import InvalidData, InconsistencyError, AuthorisationError
-from ..groups.models import Group, GroupRole, group_role_by_id
-
-from ... import db
-from ...dictify import dictify
-
-resources = Blueprint("resources", __name__)
-
-@resources.route("/categories", methods=["GET"])
-@require_oauth("profile group resource")
-def list_resource_categories() -> Response:
-    """Retrieve all resource categories"""
-    db_uri = app.config["AUTH_DB"]
-    with db.connection(db_uri) as conn:
-        return jsonify(tuple(
-            dictify(category) for category in resource_categories(conn)))
-
-@resources.route("/create", methods=["POST"])
-@require_oauth("profile group resource")
-def create_resource() -> Response:
-    """Create a new resource"""
-    with require_oauth.acquire("profile group resource") as the_token:
-        form = request.form
-        resource_name = form.get("resource_name")
-        resource_category_id = uuid.UUID(form.get("resource_category"))
-        db_uri = app.config["AUTH_DB"]
-        with db.connection(db_uri) as conn:
-            try:
-                resource = _create_resource(
-                    conn,
-                    resource_name,
-                    resource_category_by_id(conn, resource_category_id),
-                    the_token.user,
-                    (form.get("public") == "on"))
-                return jsonify(dictify(resource))
-            except sqlite3.IntegrityError as sql3ie:
-                if sql3ie.args[0] == ("UNIQUE constraint failed: "
-                                      "resources.resource_name"):
-                    raise InconsistencyError(
-                        "You cannot have duplicate resource names.") from sql3ie
-                app.logger.debug(
-                    f"{type(sql3ie)=}: {sql3ie=}")
-                raise
-
-@resources.route("/view/<uuid:resource_id>")
-@require_oauth("profile group resource")
-def view_resource(resource_id: uuid.UUID) -> Response:
-    """View a particular resource's details."""
-    with require_oauth.acquire("profile group resource") as the_token:
-        db_uri = app.config["AUTH_DB"]
-        with db.connection(db_uri) as conn:
-            return jsonify(dictify(resource_by_id(
-                conn, the_token.user, resource_id)))
-
-def __safe_get_requests_page__(key: str = "page") -> int:
-    """Get the results page if it exists or default to the first page."""
-    try:
-        return abs(int(request.args.get(key, "1"), base=10))
-    except ValueError as _valerr:
-        return 1
-
-def __safe_get_requests_count__(key: str = "count_per_page") -> int:
-    """Get the results page if it exists or default to the first page."""
-    try:
-        count = request.args.get(key, "0")
-        if count != 0:
-            return abs(int(count, base=10))
-        return 0
-    except ValueError as _valerr:
-        return 0
-
-@resources.route("/view/<uuid:resource_id>/data")
-@require_oauth("profile group resource")
-def view_resource_data(resource_id: uuid.UUID) -> Response:
-    """Retrieve a particular resource's data."""
-    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)
-        with db.connection(db_uri) as conn:
-            resource = resource_by_id(conn, the_token.user, resource_id)
-            return jsonify(resource_data(
-                conn,
-                resource,
-                ((offset * count_per_page) if bool(count_per_page) else offset),
-                count_per_page))
-
-@resources.route("/data/link", methods=["POST"])
-@require_oauth("profile group resource")
-def link_data():
-    """Link group data to a specific resource."""
-    try:
-        form = request.form
-        assert "resource_id" in form, "Resource ID not provided."
-        assert "data_link_id" in form, "Data Link ID not provided."
-        assert "dataset_type" in form, "Dataset type not specified"
-        assert form["dataset_type"].lower() in (
-            "mrna", "genotype", "phenotype"), "Invalid dataset type provided."
-
-        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.UUID(form["resource_id"]),
-                    form["dataset_type"], uuid.UUID(form["data_link_id"]))
-
-            return jsonify(with_db_connection(__link__))
-    except AssertionError as aserr:
-        raise InvalidData(aserr.args[0]) from aserr
-
-
-
-@resources.route("/data/unlink", methods=["POST"])
-@require_oauth("profile group resource")
-def unlink_data():
-    """Unlink data bound to a specific resource."""
-    try:
-        form = request.form
-        assert "resource_id" in form, "Resource ID not provided."
-        assert "data_link_id" in form, "Data Link ID not provided."
-
-        with require_oauth.acquire("profile group resource") as the_token:
-            def __unlink__(conn: db.DbConnection):
-                return unlink_data_from_resource(
-                    conn, the_token.user, uuid.UUID(form["resource_id"]),
-                    uuid.UUID(form["data_link_id"]))
-            return jsonify(with_db_connection(__unlink__))
-    except AssertionError as aserr:
-        raise InvalidData(aserr.args[0]) from aserr
-
-@resources.route("<uuid:resource_id>/user/list", methods=["GET"])
-@require_oauth("profile group resource")
-def resource_users(resource_id: uuid.UUID):
-    """Retrieve all users with access to the given resource."""
-    with require_oauth.acquire("profile group resource") as the_token:
-        def __the_users__(conn: db.DbConnection):
-            resource = resource_by_id(conn, the_token.user, resource_id)
-            authorised = authorised_for(
-                conn, the_token.user, ("group:resource:edit-resource",),
-                (resource_id,))
-            if authorised.get(resource_id, False):
-                with db.cursor(conn) as cursor:
-                    def __organise_users_n_roles__(users_n_roles, row):
-                        user_id = uuid.UUID(row["user_id"])
-                        user = users_n_roles.get(user_id, {}).get(
-                            "user", User(user_id, row["email"], row["name"]))
-                        role = GroupRole(
-                            uuid.UUID(row["group_role_id"]),
-                            resource.group,
-                            Role(uuid.UUID(row["role_id"]), row["role_name"],
-                                 bool(int(row["user_editable"])), tuple()))
-                        return {
-                            **users_n_roles,
-                            user_id: {
-                                "user": user,
-                                "user_group": Group(
-                                    uuid.UUID(row["group_id"]), row["group_name"],
-                                    json.loads(row["group_metadata"])),
-                                "roles": users_n_roles.get(
-                                    user_id, {}).get("roles", tuple()) + (role,)
-                            }
-                        }
-                    cursor.execute(
-                        "SELECT g.*, u.*, r.*, gr.group_role_id "
-                        "FROM groups AS g INNER JOIN "
-                        "group_users AS gu ON g.group_id=gu.group_id "
-                        "INNER JOIN users AS u ON gu.user_id=u.user_id "
-                        "INNER JOIN group_user_roles_on_resources AS guror "
-                        "ON u.user_id=guror.user_id INNER JOIN roles AS r "
-                        "ON guror.role_id=r.role_id "
-                        "INNER JOIN group_roles AS gr ON r.role_id=gr.role_id "
-                        "WHERE guror.resource_id=?",
-                        (str(resource_id),))
-                    return reduce(__organise_users_n_roles__, cursor.fetchall(), {})
-            raise AuthorisationError(
-                "You do not have sufficient privileges to view the resource "
-                "users.")
-        results = (
-            {
-                "user": dictify(row["user"]),
-                "user_group": dictify(row["user_group"]),
-                "roles": tuple(dictify(role) for role in row["roles"])
-            } for row in (
-                user_row for user_id, user_row
-                in with_db_connection(__the_users__).items()))
-        return jsonify(tuple(results))
-
-@resources.route("<uuid:resource_id>/user/assign", methods=["POST"])
-@require_oauth("profile group resource role")
-def assign_role_to_user(resource_id: uuid.UUID) -> Response:
-    """Assign a role on the specified resource to a user."""
-    with require_oauth.acquire("profile group resource role") as the_token:
-        try:
-            form = request.form
-            group_role_id = form.get("group_role_id", "")
-            user_email = form.get("user_email", "")
-            assert bool(group_role_id), "The role must be provided."
-            assert bool(user_email), "The user email must be provided."
-
-            def __assign__(conn: db.DbConnection) -> dict:
-                resource = resource_by_id(conn, the_token.user, resource_id)
-                user = user_by_email(conn, user_email)
-                return assign_resource_user(
-                    conn, resource, user,
-                    group_role_by_id(conn, resource.group,
-                                     uuid.UUID(group_role_id)))
-        except AssertionError as aserr:
-            raise AuthorisationError(aserr.args[0]) from aserr
-
-        return jsonify(with_db_connection(__assign__))
-
-@resources.route("<uuid:resource_id>/user/unassign", methods=["POST"])
-@require_oauth("profile group resource role")
-def unassign_role_to_user(resource_id: uuid.UUID) -> Response:
-    """Unassign a role on the specified resource from a user."""
-    with require_oauth.acquire("profile group resource role") as the_token:
-        try:
-            form = request.form
-            group_role_id = form.get("group_role_id", "")
-            user_id = form.get("user_id", "")
-            assert bool(group_role_id), "The role must be provided."
-            assert bool(user_id), "The user id must be provided."
-
-            def __assign__(conn: db.DbConnection) -> dict:
-                resource = resource_by_id(conn, the_token.user, resource_id)
-                return unassign_resource_user(
-                    conn, resource, user_by_id(conn, uuid.UUID(user_id)),
-                    group_role_by_id(conn, resource.group,
-                                     uuid.UUID(group_role_id)))
-        except AssertionError as aserr:
-            raise AuthorisationError(aserr.args[0]) from aserr
-
-        return jsonify(with_db_connection(__assign__))
-
-@resources.route("<uuid:resource_id>/toggle-public", methods=["POST"])
-@require_oauth("profile group resource role")
-def toggle_public(resource_id: uuid.UUID) -> Response:
-    """Make a resource public if it is private, or private if public."""
-    with require_oauth.acquire("profile group resource") as the_token:
-        def __toggle__(conn: db.DbConnection) -> Resource:
-            old_rsc = resource_by_id(conn, the_token.user, resource_id)
-            return save_resource(
-                conn, the_token.user, Resource(
-                    old_rsc.group, old_rsc.resource_id, old_rsc.resource_name,
-                    old_rsc.resource_category, not old_rsc.public,
-                    old_rsc.resource_data))
-
-        resource = with_db_connection(__toggle__)
-        return jsonify({
-            "resource": dictify(resource),
-            "description": (
-                "Made resource public" if resource.public
-                else "Made resource private")})