From 32aa1f463c88f16243d6840583e7a2bdd01f512d Mon Sep 17 00:00:00 2001
From: chrystinne
Date: Fri, 15 May 2026 16:37:09 -0400
Subject: [PATCH 01/15] Add disable and restore access buttons for private AWS
projects.
---
.../console/manage_published_project.html | 23 +++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/physionet-django/console/templates/console/manage_published_project.html b/physionet-django/console/templates/console/manage_published_project.html
index eee35cf0e..454f7849b 100644
--- a/physionet-django/console/templates/console/manage_published_project.html
+++ b/physionet-django/console/templates/console/manage_published_project.html
@@ -457,6 +457,29 @@ AWS
Delete project files
+ {% if project.aws.is_private %}
+ {% if project.aws.access_points.exists %}
+
+ {% else %}
+
+ {% endif %}
+ {% endif %}
{% endif %}
From 2ab0e58acd0eb78e7ccaf32a511128b8c4b06e33 Mon Sep 17 00:00:00 2001
From: chrystinne
Date: Fri, 15 May 2026 16:37:40 -0400
Subject: [PATCH 02/15] Add handlers for disabling and restoring AWS access.
---
physionet-django/console/views.py | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/physionet-django/console/views.py b/physionet-django/console/views.py
index c12994015..5abf763c2 100644
--- a/physionet-django/console/views.py
+++ b/physionet-django/console/views.py
@@ -88,6 +88,8 @@
check_s3_bucket_exists,
has_s3_credentials,
delete_project_files_from_s3,
+ disable_project_access_in_s3,
+ restore_project_access_in_s3,
)
from django.core.management import call_command
@@ -1175,6 +1177,21 @@ def manage_published_project(request, project_slug, version):
# Redirect is required after deletion to avoid rendering the page with
# a stale AWS instance that no longer has a primary key in the database.
return redirect('manage_published_project', project_slug=project_slug, version=version)
+ elif 'aws-disable-access' in request.POST and has_s3_credentials():
+ if any(get_associated_tasks(project, read_only=False)):
+ messages.error(request, 'Project has tasks pending.')
+ else:
+ disable_project_access_in_s3(project)
+ messages.success(request, 'Access to project files has been disabled.')
+ # Redirect required to avoid rendering with stale AWS instance
+ return redirect('manage_published_project', project_slug=project_slug, version=version)
+ elif 'aws-restore-access' in request.POST and has_s3_credentials():
+ if any(get_associated_tasks(project, read_only=False)):
+ messages.error(request, 'Project has tasks pending.')
+ else:
+ restore_project_access_in_s3(project)
+ messages.success(request, 'Access to project files has been restored.')
+ return redirect('manage_published_project', project_slug=project_slug, version=version)
elif 'platform' in request.POST:
data_access_form = forms.DataAccessForm(project=project, data=request.POST)
if data_access_form.is_valid():
From 5d7960f1ff67c2c7ed92c3abec5f277a8790d679 Mon Sep 17 00:00:00 2001
From: chrystinne
Date: Fri, 15 May 2026 16:38:15 -0400
Subject: [PATCH 03/15] Add support for disabling and restoring S3 access
points.
---
physionet-django/project/cloud/s3.py | 41 ++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/physionet-django/project/cloud/s3.py b/physionet-django/project/cloud/s3.py
index dce7213b8..210e09098 100644
--- a/physionet-django/project/cloud/s3.py
+++ b/physionet-django/project/cloud/s3.py
@@ -1618,3 +1618,44 @@ def delete_project_access_points(project):
# Deletes AWS, AWSAccessPoint, and AWSAccessPointUser
# from our database as well via CASCADE
project.aws.delete()
+
+
+def disable_project_access_in_s3(project):
+ """
+ Temporarily disable access to a private project by deleting its S3 access points
+ without removing the project files from the bucket.
+
+ For public projects, per-project access cannot be disabled without deleting
+ the files, since the bucket policy applies to the entire bucket.
+
+ Args:
+ project (PublishedProject): The project whose access will be disabled.
+ """
+ if not project.aws.is_private:
+ return
+ if not check_s3_bucket_exists(project):
+ return
+
+ s3control = create_s3_control_client()
+ for ap in project.aws.access_points.all():
+ s3control.delete_access_point(
+ AccountId=settings.AWS_ACCOUNT_ID,
+ Name=ap.name
+ )
+ # Only delete AWSAccessPoint records, not the AWS instance itself
+ project.aws.access_points.all().delete()
+
+
+def restore_project_access_in_s3(project):
+ """
+ Restore access to a private project by recreating its S3 access points.
+
+ Args:
+ project (PublishedProject): The project whose access will be restored.
+ """
+ if not project.aws.is_private:
+ return
+ if not check_s3_bucket_exists(project):
+ return
+
+ initialize_access_points(project)
From d2d4c301e7717aa25702ae016418e60c4214b0de Mon Sep 17 00:00:00 2001
From: chrystinne
Date: Fri, 15 May 2026 16:53:40 -0400
Subject: [PATCH 04/15] Rename 'Delete project files' button to 'Delete files'.
---
.../console/templates/console/manage_published_project.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/physionet-django/console/templates/console/manage_published_project.html b/physionet-django/console/templates/console/manage_published_project.html
index 454f7849b..a789a06ec 100644
--- a/physionet-django/console/templates/console/manage_published_project.html
+++ b/physionet-django/console/templates/console/manage_published_project.html
@@ -455,7 +455,7 @@ AWS
{% endif %}
type="submit">
- Delete project files
+ Delete files
{% if project.aws.is_private %}
{% if project.aws.access_points.exists %}
From 8ba9405526a2c161343fb7faff621e6a171885ee Mon Sep 17 00:00:00 2001
From: chrystinne
Date: Fri, 15 May 2026 16:59:27 -0400
Subject: [PATCH 05/15] Update button colors for AWS cloud management actions.
---
.../console/templates/console/manage_published_project.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/physionet-django/console/templates/console/manage_published_project.html b/physionet-django/console/templates/console/manage_published_project.html
index a789a06ec..f4a8a977a 100644
--- a/physionet-django/console/templates/console/manage_published_project.html
+++ b/physionet-django/console/templates/console/manage_published_project.html
@@ -448,7 +448,7 @@ AWS
{% endif %}
{% if project.aws.sent_files %}
-
{% if project.aws.is_private %}
- {% if project.aws.access_points.exists %}
-
+ {% if not project.aws.access_disabled %}
+ {% if project.aws.access_points.exists %}
+
+ {% endif %}
{% else %}