diff options
| author | Frederick Muriuki Muriithi | 2026-04-07 13:52:19 -0500 |
|---|---|---|
| committer | Frederick Muriuki Muriithi | 2026-04-07 13:54:02 -0500 |
| commit | b48b0eabaa3f8702366b779e6fba96c88acf9e34 (patch) | |
| tree | b932fe089406e6a8d59c25dcf2391aa5210312d8 | |
| parent | 135e861a8c09a93f4d094c8331529adadcefdba9 (diff) | |
| download | gn-auth-b48b0eabaa3f8702366b779e6fba96c88acf9e34.tar.gz | |
Update code to handle resource creators and creation times.
| -rw-r--r-- | gn_auth/auth/authorisation/resources/groups/models.py | 17 | ||||
| -rw-r--r-- | gn_auth/auth/authorisation/resources/models.py | 46 | ||||
| -rw-r--r-- | gn_auth/auth/authorisation/resources/views.py | 2 | ||||
| -rw-r--r-- | tests/unit/auth/fixtures/group_fixtures.py | 21 | ||||
| -rw-r--r-- | tests/unit/auth/fixtures/resource_fixtures.py | 48 | ||||
| -rw-r--r-- | tests/unit/auth/fixtures/role_fixtures.py | 2 | ||||
| -rw-r--r-- | tests/unit/auth/fixtures/user_fixtures.py | 21 | ||||
| -rw-r--r-- | tests/unit/auth/test_groups.py | 2 |
8 files changed, 124 insertions, 35 deletions
diff --git a/gn_auth/auth/authorisation/resources/groups/models.py b/gn_auth/auth/authorisation/resources/groups/models.py index 6a7af4c..07e6dbe 100644 --- a/gn_auth/auth/authorisation/resources/groups/models.py +++ b/gn_auth/auth/authorisation/resources/groups/models.py @@ -1,5 +1,6 @@ """Handle the management of resource/user groups.""" import json +import datetime from uuid import UUID, uuid4 from functools import reduce from dataclasses import dataclass @@ -100,8 +101,12 @@ def user_membership(conn: db.DbConnection, user: User) -> Sequence[Group]: "create a new group."), oauth2_scope="profile group") def create_group( - conn: db.DbConnection, group_name: str, group_leader: User, - group_description: Optional[str] = None) -> Group: + conn: db.DbConnection, + group_name: str, + group_leader: User, + group_description: Optional[str] = None, + creator: Optional[User] = None +) -> Group: """Create a new group.""" def resource_category_by_key( cursor: db.DbCursor, category_key: str): @@ -134,11 +139,15 @@ def create_group( resource_category_by_key( cursor, "group")["resource_category_id"] ), - "public": 0 + "public": 0, + "created_by": str( + creator.user_id if creator else group_leader.user_id), + "created_at": datetime.datetime.now().timestamp() } cursor.execute( "INSERT INTO resources VALUES " - "(:resource_id, :resource_name, :resource_category_id, :public)", + "(:resource_id, :resource_name, :resource_category_id, :public, " + ":created_by, :created_at)", _group_resource) cursor.execute( "INSERT INTO group_resources(resource_id, group_id) " diff --git a/gn_auth/auth/authorisation/resources/models.py b/gn_auth/auth/authorisation/resources/models.py index c3d0f7e..4816527 100644 --- a/gn_auth/auth/authorisation/resources/models.py +++ b/gn_auth/auth/authorisation/resources/models.py @@ -1,4 +1,5 @@ """Handle the management of resources.""" +import logging from datetime import datetime from dataclasses import asdict from uuid import UUID, uuid4 @@ -38,6 +39,9 @@ from .phenotypes.models import ( unlink_data_from_resource as phenotype_unlink_data_from_resource) +logger = logging.getLogger(__name__) + + @authorised_p(("group:resource:create-resource",), error_description="Insufficient privileges to create a resource", oauth2_scope="profile resource") @@ -129,6 +133,18 @@ def resource_categories(conn: db.DbConnection) -> Sequence[ResourceCategory]: for row in cursor.fetchall()) return tuple() + +def __fetch_creators__(cursor, creators_ids: tuple[str, ...]): + cursor.execute( + ("SELECT * FROM users " + f"WHERE user_id IN ({', '.join(['?'] * len(creators_ids))})"), + creators_ids) + return { + row["user_id"]: User.from_sqlite3_row(row) + for row in cursor.fetchall() + } + + def public_resources(conn: db.DbConnection) -> Sequence[Resource]: """List all resources marked as public""" categories = { @@ -136,10 +152,19 @@ def public_resources(conn: db.DbConnection) -> Sequence[Resource]: } with db.cursor(conn) as cursor: cursor.execute("SELECT * FROM resources WHERE public=1") - results = cursor.fetchall() + resource_rows = tuple(cursor.fetchall()) + _creators_ = __fetch_creators__( + cursor, tuple(row["created_by"] for row in resource_rows)) return tuple( - Resource(UUID(row[0]), row[1], categories[row[2]], bool(row[3])) - for row in results) + Resource( + UUID(row[0]), + row[1], + categories[row[2]], + bool(row[3]), + created_by=_creators_[row["created_by"]], + created_at=datetime.fromtimestamp(row["created_at"])) + for row in resource_rows) + def group_leader_resources( conn: db.DbConnection, user: User, group: Group, @@ -159,13 +184,14 @@ def group_leader_resources( for row in cursor.fetchall()) return tuple() + def user_resources(conn: db.DbConnection, user: User) -> Sequence[Resource]: """List the resources available to the user""" with db.cursor(conn) as cursor: cursor.execute( ("SELECT DISTINCT(r.resource_id), r.resource_name, " - "r.resource_category_id, r.public, rc.resource_category_key, " - "rc.resource_category_description " + "r.resource_category_id, r.public, r.created_by, r.created_at, " + "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 " @@ -174,7 +200,15 @@ def user_resources(conn: db.DbConnection, user: User) -> Sequence[Resource]: (str(user.user_id),)) rows = cursor.fetchall() or [] - return tuple(resource_from_dbrow(row) for row in rows) + _creators_ = __fetch_creators__( + cursor, tuple(row["created_by"] for row in rows)) + + return tuple( + Resource.from_resource( + resource_from_dbrow(row), + created_by=_creators_[row["created_by"]], + created_at=datetime.fromtimestamp(row["created_at"]) + ) for row in rows) def resource_data(conn, resource, offset: int = 0, limit: Optional[int] = None) -> tuple[dict, ...]: diff --git a/gn_auth/auth/authorisation/resources/views.py b/gn_auth/auth/authorisation/resources/views.py index e4401c5..4b6c36c 100644 --- a/gn_auth/auth/authorisation/resources/views.py +++ b/gn_auth/auth/authorisation/resources/views.py @@ -39,8 +39,6 @@ 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 .system.models import system_resource - from .inbredset.views import popbp from .genotypes.views import genobp from .phenotypes.views import phenobp diff --git a/tests/unit/auth/fixtures/group_fixtures.py b/tests/unit/auth/fixtures/group_fixtures.py index 2e8cd9a..da1c4cd 100644 --- a/tests/unit/auth/fixtures/group_fixtures.py +++ b/tests/unit/auth/fixtures/group_fixtures.py @@ -1,5 +1,6 @@ """Fixtures and utilities for group-related tests""" import uuid +import datetime import pytest @@ -7,8 +8,12 @@ from gn_auth.auth.db import sqlite3 as db from gn_auth.auth.authorisation.resources.groups import Group from gn_auth.auth.authorisation.resources import Resource, ResourceCategory +from .user_fixtures import TEST_USERS from .resource_fixtures import TEST_RESOURCES + +_created_ = datetime.datetime.now() + TEST_GROUP_01 = Group(uuid.UUID("9988c21d-f02f-4d45-8966-22c968ac2fbf"), "TheTestGroup", {}) TEST_GROUP_02 = Group(uuid.UUID("e37d59d7-c05e-4d67-b479-81e627d8d634"), @@ -24,16 +29,20 @@ GROUPS_AS_RESOURCES = tuple({ "resource_id": res_id, "resource_name": group.group_name, "category_id": str(GROUP_CATEGORY.resource_category_id), - "public": "0" + "public": "0", + "created_by": str(TEST_USERS[0].user_id), + "created_at": _created_.timestamp() } for res_id, group in zip( ("38d1807d-105f-44a7-8327-7e2d973b6d8d", "89458ef6-e090-4b53-8c2c-59eaf2785f11"), TEST_GROUPS)) GROUP_RESOURCES = tuple( - Resource(uuid.UUID(row["resource_id"]), - row["resource_name"], + Resource(uuid.UUID(row["resource_id"]),# type: ignore[arg-type] + row["resource_name"],# type: ignore[arg-type] GROUP_CATEGORY, - False) + False, + created_by=TEST_USERS[0], + created_at=_created_) for row in GROUPS_AS_RESOURCES) @@ -46,7 +55,7 @@ def __gtuple__(cursor): return tuple(dict(row) for row in cursor.fetchall()) @pytest.fixture(scope="function") -def fxtr_group(conn_after_auth_migrations):# pylint: disable=[redefined-outer-name] +def fxtr_group(conn_after_auth_migrations, fxtr_users):# pylint: disable=[redefined-outer-name, unused-argument] """Fixture: setup a test group.""" with db.cursor(conn_after_auth_migrations) as cursor: cursor.executemany( @@ -57,7 +66,7 @@ def fxtr_group(conn_after_auth_migrations):# pylint: disable=[redefined-outer-na cursor.executemany( "INSERT INTO resources " - "VALUES(:resource_id, :resource_name, :category_id, :public)", + "VALUES(:resource_id, :resource_name, :category_id, :public, :created_by, :created_at)", GROUPS_AS_RESOURCES) cursor.executemany( diff --git a/tests/unit/auth/fixtures/resource_fixtures.py b/tests/unit/auth/fixtures/resource_fixtures.py index e06f64e..b570a49 100644 --- a/tests/unit/auth/fixtures/resource_fixtures.py +++ b/tests/unit/auth/fixtures/resource_fixtures.py @@ -1,11 +1,15 @@ """Fixtures and utilities for resource-related tests""" import uuid +import datetime import pytest from gn_auth.auth.db import sqlite3 as db from gn_auth.auth.authorisation.resources import Resource, ResourceCategory +from .user_fixtures import TEST_USERS + +_created_ = datetime.datetime.now() SYSTEM_CATEGORY = ResourceCategory( uuid.UUID("aa3d787f-af6a-44fa-9b0b-c82d40e54ad2"), @@ -15,48 +19,74 @@ SYSTEM_RESOURCE = Resource( uuid.UUID("0248b289-b277-4eaa-8c94-88a434d14b6e"), "GeneNetwork System", SYSTEM_CATEGORY, - True) + True, + resource_data=tuple(), + created_by=TEST_USERS[4], + created_at=_created_) TEST_RESOURCES = ( Resource(uuid.UUID("26ad1668-29f5-439d-b905-84d551f85955"), "ResourceG01R01", ResourceCategory(uuid.UUID("48056f84-a2a6-41ac-8319-0e1e212cba2a"), "genotype", "Genotype Dataset"), - True), + True, + resource_data=tuple(), + created_by=TEST_USERS[0], + created_at=_created_), Resource(uuid.UUID("2130aec0-fefd-434d-92fd-9ca342348b2d"), "ResourceG01R02", ResourceCategory(uuid.UUID("548d684b-d4d1-46fb-a6d3-51a56b7da1b3"), "phenotype", "Phenotype (Publish) Dataset"), - False), + False, + resource_data=tuple(), + created_by=TEST_USERS[0], + created_at=_created_), Resource(uuid.UUID("e9a1184a-e8b4-49fb-b713-8d9cbeea5b83"), "ResourceG01R03", ResourceCategory(uuid.UUID("fad071a3-2fc8-40b8-992b-cdefe7dcac79"), "mrna", "mRNA Dataset"), - False), + False, + resource_data=tuple(), + created_by=TEST_USERS[0], + created_at=_created_), Resource(uuid.UUID("14496a1c-c234-49a2-978c-8859ea274054"), "ResourceG02R01", ResourceCategory(uuid.UUID("48056f84-a2a6-41ac-8319-0e1e212cba2a"), "genotype", "Genotype Dataset"), - False), + False, + resource_data=tuple(), + created_by=TEST_USERS[0], + created_at=_created_), Resource(uuid.UUID("04ad9e09-94ea-4390-8a02-11f92999806b"), "ResourceG02R02", ResourceCategory(uuid.UUID("fad071a3-2fc8-40b8-992b-cdefe7dcac79"), "mrna", "mRNA Dataset"), - True)) + True, + resource_data=tuple(), + created_by=TEST_USERS[0], + created_at=_created_)) TEST_RESOURCES_PUBLIC = (SYSTEM_RESOURCE, TEST_RESOURCES[0], TEST_RESOURCES[4]) @pytest.fixture(scope="function") -def fxtr_resources(conn_after_auth_migrations): +def fxtr_resources(conn_after_auth_migrations, fxtr_users):# pylint: disable=[unused-argument] """fixture: setup test resources in the database""" conn = conn_after_auth_migrations with db.cursor(conn) as cursor: cursor.executemany( - "INSERT INTO resources VALUES (?,?,?,?)", + "INSERT INTO resources VALUES (?,?,?,?,?,?)", ((str(res.resource_id), res.resource_name, str(res.resource_category.resource_category_id), - 1 if res.public else 0) for res in TEST_RESOURCES)) + 1 if res.public else 0, + str(res.created_by.user_id), + res.created_at.timestamp()) for res in TEST_RESOURCES)) + cursor.execute( + "UPDATE resources SET created_by=?, created_at=? " + "WHERE resource_id=?", + (str(SYSTEM_RESOURCE.created_by.user_id), + SYSTEM_RESOURCE.created_at.timestamp(), + str(SYSTEM_RESOURCE.resource_id))) yield (conn, TEST_RESOURCES) diff --git a/tests/unit/auth/fixtures/role_fixtures.py b/tests/unit/auth/fixtures/role_fixtures.py index 63a3fca..24e8e9f 100644 --- a/tests/unit/auth/fixtures/role_fixtures.py +++ b/tests/unit/auth/fixtures/role_fixtures.py @@ -108,7 +108,7 @@ def fxtr_resource_roles(fxtr_resources, fxtr_roles):# pylint: disable=[redefined @pytest.fixture(scope="function") -def fxtr_setup_group_leaders(fxtr_users): +def fxtr_setup_group_leaders(fxtr_users, fxtr_group):# pylint: disable=[unused-argument] """Define what roles users have that target resources of type 'Group'.""" conn, users = fxtr_users with db.cursor(conn) as cursor: diff --git a/tests/unit/auth/fixtures/user_fixtures.py b/tests/unit/auth/fixtures/user_fixtures.py index 1cf0e20..0872142 100644 --- a/tests/unit/auth/fixtures/user_fixtures.py +++ b/tests/unit/auth/fixtures/user_fixtures.py @@ -1,28 +1,35 @@ """Fixtures and utilities for user-related tests""" import uuid +import datetime import pytest from gn_auth.auth.db import sqlite3 as db from gn_auth.auth.authentication.users import User, hash_password +_created_ = datetime.datetime.now() + TEST_USERS = ( User(uuid.UUID("ecb52977-3004-469e-9428-2a1856725c7f"), "group@lead.er", - "Group Leader"), + "Group Leader", created=_created_), User(uuid.UUID("21351b66-8aad-475b-84ac-53ce528451e3"), - "group@mem.ber01", "Group Member 01"), + "group@mem.ber01", "Group Member 01", created=_created_), User(uuid.UUID("ae9c6245-0966-41a5-9a5e-20885a96bea7"), - "group@mem.ber02", "Group Member 02"), + "group@mem.ber02", "Group Member 02", created=_created_), User(uuid.UUID("9a0c7ce5-2f40-4e78-979e-bf3527a59579"), - "unaff@iliated.user", "Unaffiliated User")) + "unaff@iliated.user", "Unaffiliated User", created=_created_), + User(uuid.UUID("60faf8a7-832b-471e-b6a0-bd4013f1fa0e"), + "sys@admin.user", "System Admin User", created=_created_)) @pytest.fixture(scope="function") -def fxtr_users(conn_after_auth_migrations, fxtr_group):# pylint: disable=[redefined-outer-name, unused-argument] +def fxtr_users(conn_after_auth_migrations):# pylint: disable=[redefined-outer-name, unused-argument] """Fixture: setup test users.""" - query = "INSERT INTO users(user_id, email, name) VALUES (?, ?, ?)" + query = ( + "INSERT INTO users(user_id, email, name, created) VALUES (?, ?, ?, ?)") with db.cursor(conn_after_auth_migrations) as cursor: cursor.executemany(query, ( - (str(user.user_id), user.email, user.name) for user in TEST_USERS)) + (str(user.user_id), user.email, user.name, user.created.timestamp()) + for user in TEST_USERS)) yield (conn_after_auth_migrations, TEST_USERS) diff --git a/tests/unit/auth/test_groups.py b/tests/unit/auth/test_groups.py index 346beb9..6f1e8cd 100644 --- a/tests/unit/auth/test_groups.py +++ b/tests/unit/auth/test_groups.py @@ -61,6 +61,8 @@ def __cleanup_create_group__(conn, user, group): (str(user.user_id), str(grp_rsc["resource_id"]))) cursor.execute("DELETE FROM group_resources WHERE group_id=?", (str(group.group_id),)) + cursor.execute("DELETE FROM resources WHERE resource_id=?", + (grp_rsc["resource_id"],)) cursor.execute("DELETE FROM groups WHERE group_id=?", (str(group.group_id),)) |
