From 453bab568d800e3990365e84f977dea54ebdb598 Mon Sep 17 00:00:00 2001 From: Jake Rosenberg Date: Fri, 4 Oct 2024 11:14:31 -0500 Subject: [PATCH] use bulk job to set project ACLs (#1446) --- .../operations/project_system_operations.py | 50 +++++++++++++------ designsafe/apps/api/projects_v2/tasks.py | 4 +- designsafe/apps/api/projects_v2/views.py | 20 ++++---- 3 files changed, 46 insertions(+), 28 deletions(-) diff --git a/designsafe/apps/api/projects_v2/operations/project_system_operations.py b/designsafe/apps/api/projects_v2/operations/project_system_operations.py index bf10a61a09..25cf38f8d0 100644 --- a/designsafe/apps/api/projects_v2/operations/project_system_operations.py +++ b/designsafe/apps/api/projects_v2/operations/project_system_operations.py @@ -155,30 +155,39 @@ def setup_project_file_system(project_uuid: str, users: list[str]): # User creates the system and adds their credential resp = create_workspace_system(service_client, project_uuid) - for username in users: - add_user_to_project_async.apply_async(args=[project_uuid, username]) + # Add tg458981 to ensure it can read externally transferred data + acl_users = ["tg458981", *users] + + add_users_to_project_async.apply_async(args=[project_uuid, acl_users]) return resp -def add_user_to_project(project_uuid: str, username: str, set_acls=True): +def add_user_to_project(project_uuid: str, username: str): """ Give a user POSIX and Tapis permissions on a workspace system. """ service_client = service_account() system_id = f"project-{project_uuid}" logger.debug("Adding user %s to system %s", username, system_id) - if set_acls: - job_res = submit_workspace_acls_job(username, project_uuid, action="add") - logger.debug( - "Submitted workspace ACL job %s with UUID %s", job_res.name, job_res.uuid - ) + service_client.systems.shareSystem(systemId=system_id, users=[username]) set_workspace_permissions(service_client, username, system_id, role="writer") return project_uuid +def add_users_to_project(project_uuid: str, usernames: list[str]): + """Add multiple users to a project and set their ACLs using a single Tapis job.""" + for username in usernames: + add_user_to_project(project_uuid, username) + + job_res = submit_workspace_acls_job(",".join(usernames), project_uuid, action="add") + logger.debug( + "Submitted workspace ACL job %s with UUID %s", job_res.name, job_res.uuid + ) + + def remove_user_from_project(project_uuid: str, username: str): """ Unshare the system and remove all permissions and credentials. @@ -186,10 +195,6 @@ def remove_user_from_project(project_uuid: str, username: str): service_client = service_account() system_id = f"project-{project_uuid}" logger.debug("Removing user %s from system %s", username, system_id) - job_res = submit_workspace_acls_job(username, project_uuid, action="remove") - logger.debug( - "Submitted workspace ACL job %s with UUID %s", job_res.name, job_res.uuid - ) service_client.systems.unShareSystem(systemId=system_id, users=[username]) service_client.systems.revokeUserPerms( @@ -202,20 +207,33 @@ def remove_user_from_project(project_uuid: str, username: str): return project_uuid +def remove_users_from_project(project_uuid: str, usernames: list[str]): + """Remove multiple users from project, setting ACLs using a single Tapis job.""" + for username in usernames: + remove_user_from_project(project_uuid, username) + + acl_usernames = ",".join(usernames) + + job_res = submit_workspace_acls_job(acl_usernames, project_uuid, action="remove") + logger.debug( + "Submitted workspace ACL job %s with UUID %s", job_res.name, job_res.uuid + ) + + ########################################## # ASYNC TASKS FOR USER ADDITION/REMOVAL ########################################## @shared_task(bind=True) -def add_user_to_project_async(self, project_uuid: str, username: str): +def add_users_to_project_async(self, project_uuid: str, usernames: list[str]): """Async wrapper around add_user_to_project""" with AsyncTaskContext(): - add_user_to_project(project_uuid, username) + add_users_to_project(project_uuid, usernames) @shared_task(bind=True) -def remove_user_from_project_async(self, project_uuid: str, username: str): +def remove_users_from_project_async(self, project_uuid: str, usernames: str): """Async wrapper around remove_user_from_project""" with AsyncTaskContext(): - remove_user_from_project(project_uuid, username) + remove_users_from_project(project_uuid, usernames) diff --git a/designsafe/apps/api/projects_v2/tasks.py b/designsafe/apps/api/projects_v2/tasks.py index 02dc6df42b..3fac3e98cb 100644 --- a/designsafe/apps/api/projects_v2/tasks.py +++ b/designsafe/apps/api/projects_v2/tasks.py @@ -7,8 +7,8 @@ # pylint: disable=unused-import from designsafe.apps.api.projects_v2.operations.project_system_operations import ( - add_user_to_project_async, - remove_user_from_project_async, + add_users_to_project_async, + remove_users_from_project_async, ) from designsafe.apps.api.projects_v2.models.project_metadata import ProjectMetadata from designsafe.apps.api.projects_v2.operations.project_publish_operations import ( diff --git a/designsafe/apps/api/projects_v2/views.py b/designsafe/apps/api/projects_v2/views.py index 1f7791ba3e..797cbc08f8 100644 --- a/designsafe/apps/api/projects_v2/views.py +++ b/designsafe/apps/api/projects_v2/views.py @@ -41,8 +41,8 @@ from designsafe.apps.api.projects_v2.operations.project_system_operations import ( increment_workspace_count, setup_project_file_system, - add_user_to_project_async, - remove_user_from_project_async, + add_users_to_project_async, + remove_users_from_project_async, ) from designsafe.apps.api.projects_v2.schema_models.base import FileObj from designsafe.apps.api.decorators import tapis_jwt_login @@ -176,10 +176,10 @@ def put(self, request: HttpRequest, project_id: str): BaseProject.model_validate(project.value), BaseProject.model_validate(updated_project.value), ) - for user_to_add in users_to_add: - add_user_to_project_async.apply_async([project.uuid, user_to_add]) - for user_to_remove in users_to_remove: - remove_user_from_project_async.apply_async([project.uuid, user_to_remove]) + if users_to_add: + add_users_to_project_async.apply_async([project.uuid, users_to_add]) + if users_to_remove: + remove_users_from_project_async.apply_async([project.uuid, users_to_remove]) return JsonResponse({"result": "OK"}) @@ -207,10 +207,10 @@ def patch(self, request: HttpRequest, project_id: str): users_to_add, users_to_remove = get_changed_users( prev_metadata, updated_metadata ) - for user_to_add in users_to_add: - add_user_to_project_async.apply_async([project.uuid, user_to_add]) - for user_to_remove in users_to_remove: - remove_user_from_project_async.apply_async([project.uuid, user_to_remove]) + if users_to_add: + add_users_to_project_async.apply_async([project.uuid, users_to_add]) + if users_to_remove: + remove_users_from_project_async.apply_async([project.uuid, users_to_remove]) return JsonResponse({"result": "OK"})